This commit is contained in:
2026-08-04 15:41:32 +08:00
parent ec15a72b0b
commit 161bf580c7
4 changed files with 20 additions and 14 deletions
@@ -167,14 +167,16 @@ async function resetPassword(member: TenantMember) {
<div class="client-card member-table-card">
<div v-if="auth.hasPermission('company:member:write')" class="table-actions"><el-button type="primary" @click="openCreate"><Plus :size="17" />新增成员</el-button></div>
<DataTable v-model:page="memberPage" v-model:page-size="memberPageSize" class="desktop-member-table" :data="pagedMembers" :columns="memberTableColumns" :total="filteredMembers.length" row-key="id">
<template #actions="{ row }"><el-button v-if="auth.hasPermission('company:member:write')" link type="primary" @click="openEdit(row as TenantMember)"><Pencil :size="15" />编辑</el-button><el-button v-if="auth.hasPermission('company:member:reset-password')" link type="primary" @click="resetPassword(row as TenantMember)"><KeyRound :size="15" />重置密码</el-button><el-button v-if="auth.hasPermission('company:member:write') && !(row as TenantMember).isAdministrator" link :type="(row as TenantMember).status === 'ACTIVE' ? 'danger' : 'success'" @click="toggleStatus(row as TenantMember)">{{ (row as TenantMember).status === 'ACTIVE' ? '停用' : '启用' }}</el-button><span v-if="!auth.hasPermission('company:member:write') && !auth.hasPermission('company:member:reset-password')" class="readonly-action">仅查看</span></template>
<template #member="{ row }"><div class="member-identity"><span>{{ (row as TenantMember).name.slice(0, 1) }}</span><div><strong>{{ (row as TenantMember).name }}</strong><small>{{ (row as TenantMember).account }}</small></div></div></template>
<template #contact="{ row }"><div class="member-contact"><span>{{ (row as TenantMember).phone }}</span><small>{{ (row as TenantMember).email || '-' }}</small></div></template>
<template #roles="{ row }"><div class="role-tags"><el-tag v-for="name in store.roleNames((row as TenantMember).roleIds)" :key="name" effect="plain">{{ name }}</el-tag><span v-if="store.roleNames((row as TenantMember).roleIds).length === 0" class="readonly-action">未分配</span></div></template>
<template #status="{ row }"><el-tag :type="(row as TenantMember).status === 'ACTIVE' ? 'success' : 'info'" effect="light">{{ (row as TenantMember).status === 'ACTIVE' ? '正常' : '已停用' }}</el-tag></template>
<template #empty><div class="client-empty">没有匹配的企业成员,请调整筛选条件。</div></template>
</DataTable>
<div class="desktop-member-table">
<DataTable v-model:page="memberPage" v-model:page-size="memberPageSize" :data="pagedMembers" :columns="memberTableColumns" :total="filteredMembers.length" row-key="id">
<template #actions="{ row }"><el-button v-if="auth.hasPermission('company:member:write')" link type="primary" @click="openEdit(row as TenantMember)"><Pencil :size="15" />编辑</el-button><el-button v-if="auth.hasPermission('company:member:reset-password')" link type="primary" @click="resetPassword(row as TenantMember)"><KeyRound :size="15" />重置密码</el-button><el-button v-if="auth.hasPermission('company:member:write') && !(row as TenantMember).isAdministrator" link :type="(row as TenantMember).status === 'ACTIVE' ? 'danger' : 'success'" @click="toggleStatus(row as TenantMember)">{{ (row as TenantMember).status === 'ACTIVE' ? '停用' : '启用' }}</el-button><span v-if="!auth.hasPermission('company:member:write') && !auth.hasPermission('company:member:reset-password')" class="readonly-action">仅查看</span></template>
<template #member="{ row }"><div class="member-identity"><span>{{ (row as TenantMember).name.slice(0, 1) }}</span><div><strong>{{ (row as TenantMember).name }}</strong><small>{{ (row as TenantMember).account }}</small></div></div></template>
<template #contact="{ row }"><div class="member-contact"><span>{{ (row as TenantMember).phone }}</span><small>{{ (row as TenantMember).email || '-' }}</small></div></template>
<template #roles="{ row }"><div class="role-tags"><el-tag v-for="name in store.roleNames((row as TenantMember).roleIds)" :key="name" effect="plain">{{ name }}</el-tag><span v-if="store.roleNames((row as TenantMember).roleIds).length === 0" class="readonly-action">未分配</span></div></template>
<template #status="{ row }"><el-tag :type="(row as TenantMember).status === 'ACTIVE' ? 'success' : 'info'" effect="light">{{ (row as TenantMember).status === 'ACTIVE' ? '正常' : '已停用' }}</el-tag></template>
<template #empty><div class="client-empty">没有匹配的企业成员,请调整筛选条件。</div></template>
</DataTable>
</div>
<div v-if="pagedMembers.length" class="mobile-member-list">
<article v-for="member in pagedMembers" :key="member.id">
@@ -184,7 +186,7 @@ async function resetPassword(member: TenantMember) {
</article>
</div>
<div v-if="!pagedMembers.length" class="mobile-member-empty client-empty">没有匹配的企业成员,请调整筛选条件。</div>
<div v-if="!pagedMembers.length" class="mobile-member-empty">没有匹配的企业成员,请调整筛选条件。</div>
</div>
<el-drawer v-model="editorOpen" :title="editingId ? '编辑成员' : '新增成员'" size="min(520px, 94vw)" @closed="resetForm">
@@ -223,9 +225,9 @@ async function resetPassword(member: TenantMember) {
.members-page :deep(.el-form-item__error) { line-height: 1.45; overflow-wrap: anywhere; }
@media (max-width: 720px) {
.member-form-grid { grid-template-columns: 1fr; }
.desktop-member-table :deep(.el-table) { display: none; }
.desktop-member-table { display: none; }
.mobile-member-list { display: grid; gap: 10px; padding: 10px; }
.mobile-member-empty { display: grid; min-height: 150px; place-items: center; }
.mobile-member-empty { display: grid; min-height: 150px; place-items: center; border-top: 1px solid var(--border); color: #98a2a9; font-size: 13px; text-align: center; }
.mobile-member-list article { padding: 14px; border: 1px solid var(--border); background: #fff; }
.mobile-member-list header { display: flex; align-items: center; justify-content: space-between; }
.mobile-member-list dl { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0; }
@@ -92,8 +92,10 @@ test('implements member list, role assignment, password reset and status control
assert.match(membersSource, /function toggleStatus\(member: TenantMember\)/)
assert.match(membersSource, /v-model="form\.roleIds" multiple/)
assert.match(membersSource, /class="mobile-member-list"/)
assert.match(membersSource, /class="mobile-member-empty"/)
assert.doesNotMatch(membersSource, /class="mobile-member-empty client-empty"/)
assert.match(membersSource, /\.mobile-member-list, \.mobile-member-empty \{ display: none; \}/)
assert.match(membersSource, /\.mobile-member-empty \{ display: grid; min-height: 150px; place-items: center; \}/)
assert.match(membersSource, /\.mobile-member-empty \{ display: grid; min-height: 150px; place-items: center;/)
assert.match(membersSource, /store\.loadMembers\(\)/)
assert.match(membersSource, /await store\.resetMemberPassword/)
assert.match(membersSource, /await store\.toggleMemberStatus/)
@@ -108,6 +110,10 @@ test('implements member list, role assignment, password reset and status control
assert.match(membersSource, /import DataTable.*DataTable\.vue/)
assert.match(membersSource, /<QueryToolbar v-model="memberFilters"/)
assert.match(membersSource, /<DataTable v-model:page="memberPage" v-model:page-size="memberPageSize"/)
assert.match(membersSource, /<div class="desktop-member-table">\s*<DataTable/)
assert.doesNotMatch(membersSource, /<DataTable[^>]*class="desktop-member-table"/)
assert.match(membersSource, /\.desktop-member-table \{ display: none; \}/)
assert.doesNotMatch(membersSource, /\.desktop-member-table :deep\(\.el-table\)/)
assert.match(membersSource, /<section v-loading="loading" class="members-page" :aria-busy="loading">/)
assert.doesNotMatch(membersSource, /<DataTable[^>]*:loading="loading"/)
assert.match(membersSource, /fixed: 'left'/)