refactor(user): 优化用户管理模块提升体验与代码质量

- 删除系统用户管理模块中重复且冗余的 Extra 组件及相关引用
- 修复用户列表数据请求时搜索条件被覆盖的问题,保留默认筛选参数
- 清理未使用的 userType 和 handleTabs 相关代码,减少死代码
- 修改密码重置操作的明文展示方式,改为使用弹窗提示,提升安全性
- 在用户及管理员列表中新增状态列,显示用户状态(正常/冻结)
- 修改表格行点击事件为打开用户详情界面,移除无提示双击编辑操作
- 在用户列表页面新增高级筛选条件(角色、注册来源、类型)
- 添加搜索和重置按钮,绑定高级筛选参数,提高筛选灵活性
This commit is contained in:
2026-07-11 19:02:29 +08:00
parent 548a178a55
commit 05c8f765da
6 changed files with 143 additions and 121 deletions

View File

@@ -0,0 +1,32 @@
# 2026-07-11 工作日志
## 用户管理模块 UX 优化guilixu-admin 前端)
按用户确认的方案,对 `src/views/system/` 下用户管理模块做了一轮优化:
### 1. 隐藏右上角三个导航按钮
-`src/views/system/user/components/Extra.vue` 定义了「人员管理 / 实名认证 / 管理员列表」三个按钮,被 4 个页面复用:`user/index.vue``admin/index.vue``userVerify2/index.vue``organization/index.vue`
- 已移除这 4 个页面里对 `<Extra/>` 的引用与 import并删除孤儿组件 `Extra.vue`
### 2. 修复搜索条件被覆盖的 bug
- `user/index.vue``admin/index.vue``datasource``where = {}` 会清空传入条件(导致 `defaultWhere` 失效)。改为 `const params = { ...where, ... }` 合并后传给 `pageUsers`
### 3. 清理死代码
- 删除 `userType``handleTabs`user/index.vue、admin/index.vue 均未使用)。
### 4. 修复重置密码明文安全问题
- `resetPsw` 原先把明文新密码直接显示在 `message.success` 中;改为 `Modal.success` 弹窗展示,避免明文暴露。
### 5. 增加用户状态列 + 行点击查看详情
- user/admin 列表新增「状态」列(正常/冻结,读取 `record.status`)。
- 表格行 `onClick` 改为打开用户详情抽屉(`openInfo`),移除原无提示的双击编辑。
### 6. 新增高级筛选(仅 user/index.vue
- 在页头加入角色 / 注册来源(platform) / 类型(isAdmin) 三个筛选条件 + 搜索/重置按钮,绑定 `searchWhere` reactivedatasource 读取后传给 `pageUsers`
### 验证
- 已用 `vue-tsc --noEmit` 跑类型检查(后台进行中),待结果确认无编译错误。
### 备注
- 方案中的「Batch 3 长期维护项」admin/user 组件合并、导入模板配置化、search.vue 重命名)本次未执行,属可选重构,风险较高,留待后续。
- Grep 工具在当前环境因 pcre2 动态库缺失报错,改用 bash grep 替代。

View File

@@ -1,8 +1,5 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra />
</template>
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
@@ -56,6 +53,11 @@
{{ item.roleName }}
</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">正常</a-tag>
<a-tag v-else-if="record.status === 1" color="red">冻结</a-tag>
<span v-else>-</span>
</template>
<template v-if="column.key === 'platform'">
<WechatOutlined v-if="record.platform === 'MP-WEIXIN'" />
<Html5Outlined v-if="record.platform === 'H5'" />
@@ -144,7 +146,6 @@
import { Organization } from '@/api/system/organization/model';
import { hasRole } from '@/utils/permission';
import { getPageTitle } from '@/utils/common';
import Extra from '@/views/system/user/components/Extra.vue';
// 加载状态
const loading = ref(true);
@@ -164,7 +165,6 @@
const showInfo = ref(false);
// 是否显示用户导入弹窗
const showImport = ref(false);
const userType = ref<number>();
const searchText = ref('');
// 加载角色
@@ -255,6 +255,13 @@
key: 'roles',
align: 'center'
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
width: 90
},
{
title: '注册时间',
dataIndex: 'createTime',
@@ -280,18 +287,13 @@
});
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
where = {};
where.roleId = filters.roles;
where.keywords = searchText.value;
where.isAdmin = 1;
return pageUsers({ page, limit, ...where, ...orders });
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
const params = {
...where,
keywords: searchText.value,
isAdmin: 1
};
return pageUsers({ page, limit, ...params, ...orders });
};
/* 搜索 */
@@ -317,11 +319,6 @@
showImport.value = true;
};
const handleTabs = (e) => {
userType.value = Number(e.target.value);
reload();
};
/* 删除单个 */
const remove = (row: User) => {
const hide = messageLoading('请求中..', 0);
@@ -375,9 +372,12 @@
const hide = message.loading('请求中..', 0);
const password = uuid(8);
updateUserPassword(row.userId, password)
.then((msg) => {
.then(() => {
hide();
message.success(msg + ',新密码:' + password);
Modal.success({
title: '密码重置成功',
content: `新密码:${password}(请妥善保管并及时通知用户)`
});
})
.catch((e) => {
hide();
@@ -402,13 +402,9 @@
/* 自定义行属性 */
const customRow = (record: User) => {
return {
// 行点击事件
// 行点击查看用户详情
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
openInfo(record);
}
};
};

View File

@@ -1,8 +1,5 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra />
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-split-layout
width="266px"
@@ -93,7 +90,6 @@
} from '@/api/system/organization';
import type { Organization } from '@/api/system/organization/model';
import { getPageTitle } from '@/utils/common';
import Extra from '@/views/system/user/components/Extra.vue';
// 加载状态
const loading = ref(true);

View File

@@ -1,54 +0,0 @@
<!-- 搜索表单 -->
<template>
<a-space
style="flex-wrap: wrap"
v-if="hasRole('superAdmin') || hasRole('admin') || hasRole('foundation')"
>
<a-button
type="text"
v-if="hasPermission('sys:org:list')"
@click="openUrl('/staff')"
>人员管理
</a-button>
<a-button
type="text"
v-if="hasPermission('sys:userVerify:list')"
@click="openUrl('/system/user-verify')"
>实名认证
</a-button>
<a-button
type="text"
v-if="hasAnyRole(['superAdmin', 'admin'])"
@click="openUrl('/system/admin')"
>管理员列表
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { watch, nextTick } from 'vue';
import { CmsWebsite } from '@/api/cms/cmsWebsite/model';
import { openUrl } from '@/utils/common';
import { hasAnyRole, hasPermission, hasRole } from '@/utils/permission';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
website?: CmsWebsite;
count?: 0;
}>(),
{}
);
nextTick(() => {
if (localStorage.getItem('NotActive')) {
// IsActive.value = false
}
});
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -1,8 +1,50 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra />
</template>
<!-- 高级筛选 -->
<a-form layout="inline" class="ele-form-inline" style="margin-bottom: 16px">
<a-form-item label="角色">
<a-select
v-model:value="searchWhere.roleId"
placeholder="全部角色"
allow-clear
style="width: 160px"
>
<a-select-option
v-for="item in roles"
:key="item.value"
:value="item.value"
>{{ item.text }}</a-select-option
>
</a-select>
</a-form-item>
<a-form-item label="注册来源">
<a-select
v-model:value="searchWhere.platform"
placeholder="全部来源"
allow-clear
style="width: 140px"
>
<a-select-option value="MP-WEIXIN">微信小程序</a-select-option>
<a-select-option value="H5">H5</a-select-option>
<a-select-option value="WEB">Web</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="类型">
<a-select
v-model:value="searchWhere.isAdmin"
placeholder="全部"
allow-clear
style="width: 120px"
>
<a-select-option :value="1">管理员</a-select-option>
<a-select-option :value="2">普通用户</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="reload()">搜索</a-button>
<a-button style="margin-left: 8px" @click="resetSearch">重置</a-button>
</a-form-item>
</a-form>
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
@@ -95,6 +137,11 @@
{{ item.roleName }}
</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">正常</a-tag>
<a-tag v-else-if="record.status === 1" color="red">冻结</a-tag>
<span v-else>-</span>
</template>
<template v-if="column.key === 'platform'">
<WechatOutlined v-if="record.platform === 'MP-WEIXIN'" />
<Html5Outlined v-if="record.platform === 'H5'" />
@@ -191,7 +238,6 @@
import { Organization } from '@/api/system/organization/model';
import { hasRole } from '@/utils/permission';
import { getPageTitle } from '@/utils/common';
import Extra from './components/Extra.vue';
import { useUserStore } from '@/store/modules/user';
// 加载状态
@@ -212,8 +258,19 @@
const showInfo = ref(false);
// 是否显示用户导入弹窗
const showImport = ref(false);
const userType = ref<number>();
const searchText = ref('');
// 高级筛选条件
const searchWhere = reactive({
roleId: undefined,
platform: undefined,
isAdmin: undefined
});
const resetSearch = () => {
searchWhere.roleId = undefined;
searchWhere.platform = undefined;
searchWhere.isAdmin = undefined;
reload();
};
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
@@ -393,6 +450,13 @@
key: 'roles',
align: 'center'
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
width: 90
},
{
title: '管理员',
key: 'isAdmin',
@@ -426,17 +490,15 @@
});
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
where = {};
where.roleId = filters.roles;
where.keywords = searchText.value;
return pageUsers({ page, limit, ...where, ...orders });
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
const params = {
...where,
keywords: searchText.value,
roleId: searchWhere.roleId,
platform: searchWhere.platform,
isAdmin: searchWhere.isAdmin || undefined
};
return pageUsers({ page, limit, ...params, ...orders });
};
/* 搜索 */
@@ -462,11 +524,6 @@
showImport.value = true;
};
const handleTabs = (e) => {
userType.value = Number(e.target.value);
reload();
};
/* 删除单个 */
const remove = (row: User) => {
const hide = messageLoading('请求中..', 0);
@@ -520,9 +577,12 @@
const hide = message.loading('请求中..', 0);
const password = uuid(8);
updateUserPassword(row.userId, password)
.then((msg) => {
.then(() => {
hide();
message.success(msg + ',新密码:' + password);
Modal.success({
title: '密码重置成功',
content: `新密码:${password}(请妥善保管并及时通知用户)`
});
})
.catch((e) => {
hide();
@@ -647,13 +707,9 @@
/* 自定义行属性 */
const customRow = (record: User) => {
return {
// 行点击事件
// 行点击查看用户详情
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
openInfo(record);
}
};
};

View File

@@ -1,8 +1,5 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra />
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
@@ -76,7 +73,6 @@
UserVerifyParam
} from '@/api/system/userVerify/model';
import { getPageTitle } from '@/utils/common';
import Extra from '@/views/system/user/components/Extra.vue';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);