feat(log、post、user、project、work-report): 添加用户姓名字段并实现岗位用户导出功能
- 在API访问日志、API错误日志中新增用户姓名字段显示 - 优化日志管理页面布局样式,修复高度自适应问题 - 添加系统岗位数据导出功能及相应API接口 - 添加系统用户数据导出功能及相应API接口 - 新增通用文件下载和导出文件名生成工具函数 - 修复工作报表删除确认提示文案 - 调整项目列表默认展开状态逻辑 - 完善岗位和用户管理页面导出按钮权限控制 - 更新日志实体类型定义,增加用户昵称字段
This commit is contained in:
@@ -18,6 +18,29 @@ const POST_PREFIX = `${SYSTEM_SERVICE_PREFIX}/post`;
|
|||||||
const ORG_LEADER_PREFIX = `${SYSTEM_SERVICE_PREFIX}/org-leader`;
|
const ORG_LEADER_PREFIX = `${SYSTEM_SERVICE_PREFIX}/org-leader`;
|
||||||
const USER_MANAGEMENT_RELATION_PREFIX = `${SYSTEM_SERVICE_PREFIX}/user-management-relation`;
|
const USER_MANAGEMENT_RELATION_PREFIX = `${SYSTEM_SERVICE_PREFIX}/user-management-relation`;
|
||||||
|
|
||||||
|
function appendQueryValue(query: URLSearchParams, key: string, value: unknown) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach(item => appendQueryValue(query, key, item));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.append(key, String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQuery(params: object = {}) {
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
appendQueryValue(query, key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return query.toString();
|
||||||
|
}
|
||||||
|
|
||||||
function createRolePageQuery(params?: Api.SystemManage.RoleSearchParams) {
|
function createRolePageQuery(params?: Api.SystemManage.RoleSearchParams) {
|
||||||
const query = new URLSearchParams();
|
const query = new URLSearchParams();
|
||||||
|
|
||||||
@@ -469,6 +492,17 @@ export function fetchBatchDeletePost(ids: number[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 导出岗位 */
|
||||||
|
export function fetchExportPost(params: Api.SystemManage.PostSearchParams) {
|
||||||
|
const query = buildQuery(params);
|
||||||
|
|
||||||
|
return request<Blob, 'blob'>({
|
||||||
|
url: query ? `${POST_PREFIX}/export-excel?${query}` : `${POST_PREFIX}/export-excel`,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取用户简单列表(用于用户选择下拉框) */
|
/** 获取用户简单列表(用于用户选择下拉框) */
|
||||||
export async function fetchGetUserSimpleList() {
|
export async function fetchGetUserSimpleList() {
|
||||||
return request<UserSimpleResponse[]>({
|
return request<UserSimpleResponse[]>({
|
||||||
@@ -579,6 +613,17 @@ export function fetchBatchDeleteUser(ids: number[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 导出用户 */
|
||||||
|
export function fetchExportUser(params: Api.SystemManage.UserSearchParams) {
|
||||||
|
const query = buildQuery(params);
|
||||||
|
|
||||||
|
return request<Blob, 'blob'>({
|
||||||
|
url: query ? `${USER_PREFIX}/export-excel?${query}` : `${USER_PREFIX}/export-excel`,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取菜单列表 */
|
/** 获取菜单列表 */
|
||||||
export async function fetchGetMenuList(params?: Api.SystemManage.MenuSearchParams) {
|
export async function fetchGetMenuList(params?: Api.SystemManage.MenuSearchParams) {
|
||||||
const result = await request<MenuResponse[]>({
|
const result = await request<MenuResponse[]>({
|
||||||
|
|||||||
2
src/typings/api/system-log.d.ts
vendored
2
src/typings/api/system-log.d.ts
vendored
@@ -77,6 +77,7 @@ declare namespace Api {
|
|||||||
id: string;
|
id: string;
|
||||||
traceId?: string | null;
|
traceId?: string | null;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
userNickname?: string | null;
|
||||||
userType: number;
|
userType: number;
|
||||||
applicationName: string;
|
applicationName: string;
|
||||||
requestMethod: string;
|
requestMethod: string;
|
||||||
@@ -114,6 +115,7 @@ declare namespace Api {
|
|||||||
id: string;
|
id: string;
|
||||||
traceId?: string | null;
|
traceId?: string | null;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
userNickname?: string | null;
|
||||||
userType: number;
|
userType: number;
|
||||||
applicationName: string;
|
applicationName: string;
|
||||||
requestMethod: string;
|
requestMethod: string;
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ const detailSections: LogDetailSection[] = [
|
|||||||
{
|
{
|
||||||
title: '业务上下文',
|
title: '业务上下文',
|
||||||
fields: [
|
fields: [
|
||||||
|
{ label: '用户姓名', key: 'userNickname' },
|
||||||
{ label: '用户编号', key: 'userId' },
|
{ label: '用户编号', key: 'userId' },
|
||||||
{ label: '操作模块', key: 'operateModule' },
|
{ label: '操作模块', key: 'operateModule' },
|
||||||
{ label: '操作名', key: 'operateName' },
|
{ label: '操作名', key: 'operateName' },
|
||||||
@@ -114,6 +115,13 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
|
|||||||
columns: () => [
|
columns: () => [
|
||||||
{ prop: 'index', type: 'index', label: '序号', width: 64 },
|
{ prop: 'index', type: 'index', label: '序号', width: 64 },
|
||||||
{ prop: 'applicationName', label: '应用名', minWidth: 140, showOverflowTooltip: true },
|
{ prop: 'applicationName', label: '应用名', minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{
|
||||||
|
prop: 'userNickname',
|
||||||
|
label: '用户姓名',
|
||||||
|
minWidth: 120,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: row => row.userNickname || '--'
|
||||||
|
},
|
||||||
{ prop: 'requestMethod', label: '请求方式', width: 100, align: 'center' },
|
{ prop: 'requestMethod', label: '请求方式', width: 100, align: 'center' },
|
||||||
{ prop: 'requestUrl', label: '请求地址', minWidth: 220, showOverflowTooltip: true },
|
{ prop: 'requestUrl', label: '请求地址', minWidth: 220, showOverflowTooltip: true },
|
||||||
{ prop: 'operateModule', label: '操作模块', minWidth: 140, showOverflowTooltip: true },
|
{ prop: 'operateModule', label: '操作模块', minWidth: 140, showOverflowTooltip: true },
|
||||||
@@ -182,7 +190,7 @@ async function handleExport() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px overflow-hidden">
|
<div class="h-full min-h-0 flex-col-stretch gap-16px overflow-hidden">
|
||||||
<ApiAccessLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
<ApiAccessLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
||||||
|
|
||||||
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ const detailSections: LogDetailSection[] = [
|
|||||||
{
|
{
|
||||||
title: '上下文',
|
title: '上下文',
|
||||||
fields: [
|
fields: [
|
||||||
|
{ label: '用户姓名', key: 'userNickname' },
|
||||||
{ label: '用户编号', key: 'userId' },
|
{ label: '用户编号', key: 'userId' },
|
||||||
{ label: '用户IP', key: 'userIp' },
|
{ label: '用户IP', key: 'userIp' },
|
||||||
{ label: '浏览器UA', key: 'userAgent', type: 'multiline' },
|
{ label: '浏览器UA', key: 'userAgent', type: 'multiline' },
|
||||||
@@ -109,6 +110,13 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
|
|||||||
columns: () => [
|
columns: () => [
|
||||||
{ prop: 'index', type: 'index', label: '序号', width: 64 },
|
{ prop: 'index', type: 'index', label: '序号', width: 64 },
|
||||||
{ prop: 'applicationName', label: '应用名', minWidth: 140, showOverflowTooltip: true },
|
{ prop: 'applicationName', label: '应用名', minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{
|
||||||
|
prop: 'userNickname',
|
||||||
|
label: '用户姓名',
|
||||||
|
minWidth: 120,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: row => row.userNickname || '--'
|
||||||
|
},
|
||||||
{ prop: 'requestMethod', label: '请求方式', width: 100, align: 'center' },
|
{ prop: 'requestMethod', label: '请求方式', width: 100, align: 'center' },
|
||||||
{ prop: 'requestUrl', label: '请求地址', minWidth: 220, showOverflowTooltip: true },
|
{ prop: 'requestUrl', label: '请求地址', minWidth: 220, showOverflowTooltip: true },
|
||||||
{ prop: 'exceptionName', label: '异常名', minWidth: 180, showOverflowTooltip: true },
|
{ prop: 'exceptionName', label: '异常名', minWidth: 180, showOverflowTooltip: true },
|
||||||
@@ -174,7 +182,7 @@ async function handleExport() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px overflow-hidden">
|
<div class="h-full min-h-0 flex-col-stretch gap-16px overflow-hidden">
|
||||||
<ApiErrorLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
<ApiErrorLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
||||||
|
|
||||||
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
||||||
|
|||||||
@@ -67,11 +67,13 @@ watch(
|
|||||||
</ElCard>
|
</ElCard>
|
||||||
|
|
||||||
<div v-if="activeTabMeta" class="log-management-page__content">
|
<div v-if="activeTabMeta" class="log-management-page__content">
|
||||||
|
<div class="log-management-page__pane">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="componentMap[activeTab]" />
|
<component :is="componentMap[activeTab]" class="log-management-page__tab" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -157,8 +159,26 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.log-management-page__content {
|
.log-management-page__content {
|
||||||
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-management-page__pane {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.log-management-page__tab) {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 1200px) {
|
@media (width <= 1200px) {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ async function handleExport() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px overflow-hidden">
|
<div class="h-full min-h-0 flex-col-stretch gap-16px overflow-hidden">
|
||||||
<LoginLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
<LoginLogSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
||||||
|
|
||||||
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ async function handleExport() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col-stretch gap-16px overflow-hidden">
|
<div class="h-full min-h-0 flex-col-stretch gap-16px overflow-hidden">
|
||||||
<OperateLogSearch
|
<OperateLogSearch
|
||||||
v-model:model="searchParams"
|
v-model:model="searchParams"
|
||||||
:user-options="userOptions"
|
:user-options="userOptions"
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ async function handleSubmitReport(row: Api.WorkReport.Monthly.MonthlyReport) {
|
|||||||
|
|
||||||
async function handleDelete(row: Api.WorkReport.Monthly.MonthlyReport) {
|
async function handleDelete(row: Api.WorkReport.Monthly.MonthlyReport) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确认删除 ${formatPeriod(row)} 吗?`, '删除确认', {
|
await ElMessageBox.confirm(`确认删除该报告吗?`, '删除确认', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
cancelButtonText: '取消'
|
cancelButtonText: '取消'
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ async function handleSubmitReport(row: Api.WorkReport.Project.ProjectReport) {
|
|||||||
|
|
||||||
async function handleDelete(row: Api.WorkReport.Project.ProjectReport) {
|
async function handleDelete(row: Api.WorkReport.Project.ProjectReport) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确认删除 ${formatPeriod(row)} 吗?`, '删除确认', {
|
await ElMessageBox.confirm(`确认删除该报告吗?`, '删除确认', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
cancelButtonText: '取消'
|
cancelButtonText: '取消'
|
||||||
|
|||||||
@@ -420,6 +420,11 @@ async function refreshDraft() {
|
|||||||
|
|
||||||
if (result.error || !result.data) return;
|
if (result.error || !result.data) return;
|
||||||
|
|
||||||
|
baseInfo.value = {
|
||||||
|
...(baseInfo.value || {}),
|
||||||
|
...result.data
|
||||||
|
} as WorkReportRow;
|
||||||
|
|
||||||
if (props.reportType === 'weekly') {
|
if (props.reportType === 'weekly') {
|
||||||
applyWeeklyEditableFields(result.data as Api.WorkReport.Weekly.WeeklyReport);
|
applyWeeklyEditableFields(result.data as Api.WorkReport.Weekly.WeeklyReport);
|
||||||
} else if (props.reportType === 'monthly') {
|
} else if (props.reportType === 'monthly') {
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ async function handleSubmitReport(row: Api.WorkReport.Weekly.WeeklyReport) {
|
|||||||
|
|
||||||
async function handleDelete(row: Api.WorkReport.Weekly.WeeklyReport) {
|
async function handleDelete(row: Api.WorkReport.Weekly.WeeklyReport) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确认删除 ${formatPeriod(row)} 吗?`, '删除确认', {
|
await ElMessageBox.confirm(`确认删除该报告吗?`, '删除确认', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
cancelButtonText: '取消'
|
cancelButtonText: '取消'
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const searchParams = reactive(getInitSearchParams());
|
|||||||
const navKey = ref<ProjectListNavKey>('active');
|
const navKey = ref<ProjectListNavKey>('active');
|
||||||
const groupPage = ref<Api.Project.ProjectGroupPageResult>(createEmptyGroupPage());
|
const groupPage = ref<Api.Project.ProjectGroupPageResult>(createEmptyGroupPage());
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const allCollapsed = ref(true);
|
const allCollapsed = ref(false);
|
||||||
/** 状态看板项(overview-summary items,状态机动态下发),左栏与"全部"口径派生均以此为源 */
|
/** 状态看板项(overview-summary items,状态机动态下发),左栏与"全部"口径派生均以此为源 */
|
||||||
const statusBoardItems = ref<Api.Project.OverviewStatusItem[]>([]);
|
const statusBoardItems = ref<Api.Project.OverviewStatusItem[]>([]);
|
||||||
/** "全部"口径总数(后端 total,前端不自行求和) */
|
/** "全部"口径总数(后端 total,前端不自行求和) */
|
||||||
@@ -171,7 +171,7 @@ async function handleResetSearch() {
|
|||||||
|
|
||||||
async function handleNavChange(key: ProjectListNavKey) {
|
async function handleNavChange(key: ProjectListNavKey) {
|
||||||
navKey.value = key;
|
navKey.value = key;
|
||||||
allCollapsed.value = true;
|
allCollapsed.value = false;
|
||||||
await Promise.all([loadOverviewData(), loadGroupPage(1)]);
|
await Promise.all([loadOverviewData(), loadGroupPage(1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import type { TableInstance } from 'element-plus';
|
|||||||
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
|
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useBoolean } from '@sa/hooks';
|
import { useBoolean } from '@sa/hooks';
|
||||||
import { fetchBatchDeletePost, fetchDeletePost, fetchGetPostPage } from '@/service/api';
|
import { fetchBatchDeletePost, fetchDeletePost, fetchExportPost, fetchGetPostPage } from '@/service/api';
|
||||||
import { useUIPaginatedTable } from '@/hooks/common/table';
|
import { useUIPaginatedTable } from '@/hooks/common/table';
|
||||||
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
|
||||||
|
import { downloadBlob, getSystemExportFileName } from '@/views/system/shared/export';
|
||||||
import PostOperateDialog from './modules/post-operate-dialog.vue';
|
import PostOperateDialog from './modules/post-operate-dialog.vue';
|
||||||
import PostSearch from './modules/post-search.vue';
|
import PostSearch from './modules/post-search.vue';
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ function getPostTypeLabel(type?: Api.SystemManage.PostType | null) {
|
|||||||
const searchParams = reactive(getInitSearchParams());
|
const searchParams = reactive(getInitSearchParams());
|
||||||
const postTableRef = ref<TableInstance>();
|
const postTableRef = ref<TableInstance>();
|
||||||
const postCheckedRowKeys = ref<number[]>([]);
|
const postCheckedRowKeys = ref<number[]>([]);
|
||||||
|
const exporting = ref(false);
|
||||||
|
|
||||||
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useUIPaginatedTable({
|
const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagination } = useUIPaginatedTable({
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
@@ -222,6 +224,18 @@ async function reloadPostTable(page = searchParams.pageNo) {
|
|||||||
postTableRef.value?.clearSelection();
|
postTableRef.value?.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleExport() {
|
||||||
|
exporting.value = true;
|
||||||
|
const { error, data: blob } = await fetchExportPost(searchParams);
|
||||||
|
exporting.value = false;
|
||||||
|
|
||||||
|
if (error || !blob) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadBlob(blob, getSystemExportFileName('岗位数据'));
|
||||||
|
}
|
||||||
|
|
||||||
function resetSearchParams() {
|
function resetSearchParams() {
|
||||||
Object.assign(searchParams, getInitSearchParams());
|
Object.assign(searchParams, getInitSearchParams());
|
||||||
reloadPostTable(1);
|
reloadPostTable(1);
|
||||||
@@ -255,6 +269,12 @@ function handleSubmitted(postId: number) {
|
|||||||
@refresh="reloadPostTable"
|
@refresh="reloadPostTable"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
|
<ElButton v-auth="'system:post:export'" plain type="primary" :loading="exporting" @click="handleExport">
|
||||||
|
<template #icon>
|
||||||
|
<icon-mdi-download class="text-icon" />
|
||||||
|
</template>
|
||||||
|
导出
|
||||||
|
</ElButton>
|
||||||
<ElButton plain type="primary" @click="openAdd">
|
<ElButton plain type="primary" @click="openAdd">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-plus class="text-icon" />
|
<icon-ic-round-plus class="text-icon" />
|
||||||
|
|||||||
16
src/views/system/shared/export.ts
Normal file
16
src/views/system/shared/export.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export function downloadBlob(blob: Blob, fileName: string) {
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
|
||||||
|
link.href = url;
|
||||||
|
link.download = fileName;
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSystemExportFileName(label: string) {
|
||||||
|
return `${label}_${dayjs().format('YYYY-MM-DD')}.xls`;
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
fetchBatchDeleteUser,
|
fetchBatchDeleteUser,
|
||||||
fetchDeleteDept,
|
fetchDeleteDept,
|
||||||
fetchDeleteUser,
|
fetchDeleteUser,
|
||||||
|
fetchExportUser,
|
||||||
fetchGetDeptList,
|
fetchGetDeptList,
|
||||||
fetchGetDictDataByCode,
|
fetchGetDictDataByCode,
|
||||||
fetchGetPostSimpleList,
|
fetchGetPostSimpleList,
|
||||||
@@ -23,6 +24,7 @@ import BusinessTableActionCell from '@/components/custom/business-table-action-c
|
|||||||
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { buildMenuTree } from '@/views/system/shared/menu-tree';
|
import { buildMenuTree } from '@/views/system/shared/menu-tree';
|
||||||
|
import { downloadBlob, getSystemExportFileName } from '@/views/system/shared/export';
|
||||||
import UserManagementRelation from '@/views/system/user-management-relation/index.vue';
|
import UserManagementRelation from '@/views/system/user-management-relation/index.vue';
|
||||||
import UserOperateDialog from './modules/user-operate-dialog.vue';
|
import UserOperateDialog from './modules/user-operate-dialog.vue';
|
||||||
import UserOrgLeaderDialog from './modules/user-org-leader-dialog.vue';
|
import UserOrgLeaderDialog from './modules/user-org-leader-dialog.vue';
|
||||||
@@ -130,6 +132,7 @@ const deptLoading = ref(false);
|
|||||||
const userTableRef = ref<TableInstance>();
|
const userTableRef = ref<TableInstance>();
|
||||||
const userCheckedRowKeys = ref<number[]>([]);
|
const userCheckedRowKeys = ref<number[]>([]);
|
||||||
const statusLoadingIds = ref<number[]>([]);
|
const statusLoadingIds = ref<number[]>([]);
|
||||||
|
const exporting = ref(false);
|
||||||
const deptList = ref<Api.SystemManage.Dept[]>([]);
|
const deptList = ref<Api.SystemManage.Dept[]>([]);
|
||||||
const currentDeptId = ref<number | null>(null);
|
const currentDeptId = ref<number | null>(null);
|
||||||
const operateVisible = ref(false);
|
const operateVisible = ref(false);
|
||||||
@@ -392,6 +395,25 @@ async function reloadUserTable(page = searchParams.pageNo) {
|
|||||||
userTableRef.value?.clearSelection();
|
userTableRef.value?.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleExport() {
|
||||||
|
if (!currentDeptId.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exporting.value = true;
|
||||||
|
const { error, data: blob } = await fetchExportUser({
|
||||||
|
...searchParams,
|
||||||
|
deptId: currentDeptId.value
|
||||||
|
});
|
||||||
|
exporting.value = false;
|
||||||
|
|
||||||
|
if (error || !blob) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadBlob(blob, getSystemExportFileName('用户数据'));
|
||||||
|
}
|
||||||
|
|
||||||
function handleDeptSelect(nodeData: Api.SystemManage.Dept) {
|
function handleDeptSelect(nodeData: Api.SystemManage.Dept) {
|
||||||
currentDeptId.value = nodeData.id;
|
currentDeptId.value = nodeData.id;
|
||||||
}
|
}
|
||||||
@@ -669,6 +691,19 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
<TableHeaderOperation v-model:columns="columnChecks" :loading="loading" @refresh="reloadUserTable">
|
<TableHeaderOperation v-model:columns="columnChecks" :loading="loading" @refresh="reloadUserTable">
|
||||||
<template #default>
|
<template #default>
|
||||||
|
<ElButton
|
||||||
|
v-auth="'system:user:export'"
|
||||||
|
plain
|
||||||
|
type="primary"
|
||||||
|
:loading="exporting"
|
||||||
|
:disabled="!currentDept"
|
||||||
|
@click="handleExport"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-mdi-download class="text-icon" />
|
||||||
|
</template>
|
||||||
|
导出
|
||||||
|
</ElButton>
|
||||||
<ElButton plain type="primary" :disabled="!currentDept" @click="openAdd">
|
<ElButton plain type="primary" :disabled="!currentDept" @click="openAdd">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-plus class="text-icon" />
|
<icon-ic-round-plus class="text-icon" />
|
||||||
|
|||||||
Reference in New Issue
Block a user