feat: 扩展用户管理统一搜索范围

- 支持按账号、昵称、电话、邮件和角色名搜索

- 补充搜索提示、特殊字符处理及前后端测试
This commit is contained in:
2026-08-07 12:14:55 +08:00
parent 6544cdcff0
commit 170b315459
6 changed files with 252 additions and 1 deletions

View File

@@ -3,6 +3,7 @@
"deptId": "Dept",
"tenantId": "TenantId",
"loginName": "LoginName",
"searchPlaceholder": "Search account, nickname, phone, email, or role",
"password": "Password",
"accountType": "AccountType",
"nickname": "Nickname",

View File

@@ -3,6 +3,7 @@
"deptId": "部门",
"tenantId": "租户ID",
"loginName": "登录账号",
"searchPlaceholder": "搜索账号、昵称、电话、邮件或角色",
"password": "密码",
"accountType": "账户类型",
"nickname": "昵称",

View File

@@ -0,0 +1,123 @@
/* eslint-disable vue/one-component-per-file -- Inline stubs keep this search test isolated. */
import { mount } from '@vue/test-utils';
import { defineComponent, h } from 'vue';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import SysAccountList from './SysAccountList.vue';
const pageDataMocks = vi.hoisted(() => ({
setQuery: vi.fn(),
}));
const dictStoreMocks = vi.hoisted(() => ({
fetchDictionary: vi.fn(),
}));
vi.mock('#/api/request', () => ({
api: {
get: vi.fn(),
post: vi.fn(),
},
}));
vi.mock('#/locales', () => ({
$t: (key: string) => {
if (key === 'sysAccount.searchPlaceholder') {
return '搜索账号、昵称、电话、邮件或角色';
}
return key;
},
}));
vi.mock('#/store', () => ({
useDictStore: () => dictStoreMocks,
}));
vi.mock('#/components/headerSearch/HeaderSearch.vue', () => ({
default: defineComponent({
name: 'HeaderSearch',
props: {
buttons: {
type: Array,
default: () => [],
},
searchPlaceholder: {
type: String,
default: '',
},
},
emits: ['buttonClick', 'search'],
setup(props, { emit }) {
return () =>
h(
'button',
{
'data-placeholder': props.searchPlaceholder,
'data-testid': 'account-search',
onClick: () => emit('search', ' alice@example.com '),
},
props.searchPlaceholder,
);
},
}),
}));
vi.mock('#/components/page/ListPageShell.vue', () => ({
default: defineComponent({
name: 'ListPageShell',
setup(_, { slots }) {
return () => h('div', [slots.filters?.(), slots.default?.()]);
},
}),
}));
vi.mock('#/components/page/PageData.vue', () => ({
default: defineComponent({
name: 'PageData',
setup(_, { expose }) {
expose(pageDataMocks);
return () => h('div');
},
}),
}));
vi.mock('./SysAccountImportModal.vue', () => ({
default: defineComponent({
name: 'SysAccountImportModal',
setup: () => () => h('div'),
}),
}));
vi.mock('./SysAccountModal.vue', () => ({
default: defineComponent({
name: 'SysAccountModal',
setup: () => () => h('div'),
}),
}));
vi.mock('./SysAccountPasswordResultModal.vue', () => ({
default: defineComponent({
name: 'SysAccountPasswordResultModal',
setup: () => () => h('div'),
}),
}));
describe('sysAccountList search', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('submits a trimmed unified keyword and describes searchable fields', async () => {
const wrapper = mount(SysAccountList, {
global: {
directives: {
access: {},
loading: {},
},
},
});
const search = wrapper.get('[data-testid="account-search"]');
expect(search.attributes('data-placeholder')).toBe(
'搜索账号、昵称、电话、邮件或角色',
);
await search.trigger('click');
expect(pageDataMocks.setQuery).toHaveBeenCalledWith({
keyword: 'alice@example.com',
});
wrapper.unmount();
});
});

View File

@@ -69,7 +69,7 @@ function initDict() {
dictStore.fetchDictionary('dataStatus');
}
const handleSearch = (params: string) => {
pageDataRef.value.setQuery({ loginName: params, isQueryOr: true });
pageDataRef.value.setQuery({ keyword: params.trim() });
};
function clearSelection() {
selectedRows.value = [];
@@ -276,6 +276,7 @@ function isAdmin(data: any) {
<template #filters>
<HeaderSearch
:buttons="headerButtons"
:search-placeholder="$t('sysAccount.searchPlaceholder')"
@search="handleSearch"
@button-click="handleHeaderButtonClick"
>