优化页面
This commit is contained in:
@@ -93,6 +93,10 @@ describe('InternalUsersView unified platform user management', () => {
|
||||
expect(wrapper.text()).toContain('华远贸易')
|
||||
expect(wrapper.text()).toContain('租户管理员')
|
||||
expect(wrapper.text()).toContain('2026-06-01 10:30')
|
||||
expect(wrapper.find('[aria-label="用户筛选"]').exists()).toBe(true)
|
||||
expect(wrapper.find('[aria-label="用户列表"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('查询')
|
||||
expect(wrapper.text()).toContain('重置')
|
||||
expect(wrapper.text()).not.toContain('认证用户 ID')
|
||||
expect(wrapper.text()).not.toContain('成员编号')
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ import { getApiErrorDetails, getApiErrorMessage, type ApiErrorDetails } from '@/
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { useAuthStore } from '@/core/auth/auth'
|
||||
@@ -33,7 +34,37 @@ const editingUser = ref<PlatformManagedUser | null>(null)
|
||||
const rowActionId = ref('')
|
||||
const page = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const filters = reactive({ keyword: '', tenantId: '', status: '' })
|
||||
type UserFilters = QueryModel & { keyword: string; tenantId: string; status: string }
|
||||
const filters = ref<UserFilters>({ keyword: '', tenantId: '', status: '' })
|
||||
const appliedFilters = ref<UserFilters>({ ...filters.value })
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
key: 'keyword',
|
||||
type: 'input',
|
||||
placeholder: '用户名、姓名、手机号、租户或角色',
|
||||
props: { maxlength: 128, 'aria-label': '用户关键词' },
|
||||
},
|
||||
{
|
||||
key: 'tenantId',
|
||||
type: 'select',
|
||||
placeholder: '全部租户',
|
||||
props: { 'aria-label': '所属租户' },
|
||||
options: options.value.tenants.map((tenant) => ({
|
||||
label: tenant.name,
|
||||
value: String(tenant.id),
|
||||
})),
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
type: 'select',
|
||||
placeholder: '全部状态',
|
||||
props: { 'aria-label': '用户状态' },
|
||||
options: [
|
||||
{ label: '正常', value: 'ACTIVE' },
|
||||
{ label: '禁用', value: 'DISABLED' },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const emptyForm = () => ({
|
||||
username: '',
|
||||
@@ -96,13 +127,13 @@ const formRules = computed<FormRules>(() => ({
|
||||
}))
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
const keyword = filters.keyword.trim().toLowerCase()
|
||||
const keyword = appliedFilters.value.keyword.trim().toLowerCase()
|
||||
return users.value.filter((item) => {
|
||||
const matchesKeyword = !keyword || [item.username, item.displayName, item.phone ?? '', item.email ?? '', item.tenantName, ...item.roleNames]
|
||||
.some((value) => value.toLowerCase().includes(keyword))
|
||||
return matchesKeyword
|
||||
&& (!filters.tenantId || String(item.tenantId) === filters.tenantId)
|
||||
&& (!filters.status || item.status === filters.status)
|
||||
&& (!appliedFilters.value.tenantId || String(item.tenantId) === appliedFilters.value.tenantId)
|
||||
&& (!appliedFilters.value.status || item.status === appliedFilters.value.status)
|
||||
})
|
||||
})
|
||||
const pagedRows = computed(() => filteredRows.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value))
|
||||
@@ -247,10 +278,8 @@ function formatLogin(value: string | null) {
|
||||
return value.replace('T', ' ').slice(0, 16)
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
filters.keyword = ''
|
||||
filters.tenantId = ''
|
||||
filters.status = ''
|
||||
function applyFilters() {
|
||||
appliedFilters.value = { ...filters.value }
|
||||
page.value = 1
|
||||
}
|
||||
|
||||
@@ -266,26 +295,25 @@ onMounted(loadUsers)
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<section class="surface-panel table-panel">
|
||||
<QueryToolbar
|
||||
v-model="filters"
|
||||
class="surface-panel"
|
||||
ariaLabel="用户筛选"
|
||||
:fields="queryFields"
|
||||
@search="applyFilters"
|
||||
/>
|
||||
|
||||
<section class="surface-panel table-panel" :aria-busy="loading">
|
||||
<header class="panel-heading">
|
||||
<div><h2>用户管理 <span class="secondary-text">共 {{ filteredRows.length }} 条</span></h2></div>
|
||||
<div>
|
||||
<h2>用户列表 <span class="secondary-text">共 {{ filteredRows.length }} 条</span></h2>
|
||||
<p>统一维护平台与企业租户账号、所属角色和使用状态。</p>
|
||||
</div>
|
||||
<el-button v-if="canCreate" type="primary" @click="openCreate"><Plus :size="16" />新增用户</el-button>
|
||||
</header>
|
||||
|
||||
<div class="query-toolbar">
|
||||
<el-input v-model="filters.keyword" clearable placeholder="用户名、姓名、手机号、租户或角色" @keyup.enter="page = 1" />
|
||||
<el-select v-model="filters.tenantId" clearable placeholder="全部租户" @change="page = 1">
|
||||
<el-option v-for="tenant in options.tenants" :key="tenant.id" :label="tenant.name" :value="String(tenant.id)" />
|
||||
</el-select>
|
||||
<el-select v-model="filters.status" clearable placeholder="全部状态" @change="page = 1">
|
||||
<el-option label="正常" value="ACTIVE" />
|
||||
<el-option label="禁用" value="DISABLED" />
|
||||
</el-select>
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</div>
|
||||
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="pagedRows" :columns="userColumns"
|
||||
:loading="loading" :total="filteredRows.length" row-key="membershipId" empty-text="暂无用户数据">
|
||||
:loading="loading" :total="filteredRows.length" aria-label="用户列表" row-key="membershipId" empty-text="没有符合筛选条件的用户">
|
||||
<template #actions="{ row }">
|
||||
<div class="table-actions">
|
||||
<el-button v-if="canEdit || canChangeStatus || canResetPassword" link type="primary" @click="openEdit(row as PlatformManagedUser)">
|
||||
@@ -369,9 +397,8 @@ onMounted(loadUsers)
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.query-toolbar { display: grid; grid-template-columns: minmax(240px, 1fr) 220px 160px auto; gap: 12px; padding: 0 20px 16px; }
|
||||
.password-fields { margin-top: 16px; }
|
||||
@media (max-width: 720px) {
|
||||
.query-toolbar, .form-grid.two-cols { grid-template-columns: 1fr; }
|
||||
.form-grid.two-cols { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user