优化
This commit is contained in:
@@ -30,19 +30,19 @@ const memberRules: FormRules = {
|
||||
{ required: true, message: '请输入初始密码', trigger: 'blur' },
|
||||
{ pattern: /^(?=.*[A-Za-z])(?=.*\d).{8,64}$/, message: '密码需为 8–64 位,并同时包含字母和数字', trigger: 'blur' },
|
||||
],
|
||||
phone: [{ max: 32, message: '联系电话不能超过 32 个字符', trigger: 'blur' }],
|
||||
phone: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的中国大陆登录手机号', trigger: 'blur' }],
|
||||
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }],
|
||||
roleIds: [{ type: 'array', required: true, min: 1, message: '请至少选择一个成员角色', trigger: 'change' }],
|
||||
}
|
||||
|
||||
const memberQueryFields: QueryField[] = [
|
||||
{ key: 'keyword', type: 'input', placeholder: '姓名、账号、手机或邮箱' },
|
||||
{ key: 'keyword', type: 'input', placeholder: '姓名、账号、登录手机号或邮箱' },
|
||||
{ key: 'status', type: 'select', placeholder: '全部状态', options: [{ label: '正常', value: 'ACTIVE' }, { label: '已停用', value: 'INACTIVE' }] },
|
||||
]
|
||||
const memberTableColumns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', fixed: 'left', width: 230, slotName: 'actions' },
|
||||
{ key: 'member', label: '成员', minWidth: 170, slotName: 'member' },
|
||||
{ key: 'contact', label: '联系方式', minWidth: 200, slotName: 'contact' },
|
||||
{ key: 'contact', label: '登录手机号 / 邮箱', minWidth: 200, slotName: 'contact' },
|
||||
{ key: 'roles', label: '角色', minWidth: 130, slotName: 'roles' },
|
||||
{ key: 'lastLoginAt', prop: 'lastLoginAt', label: '登录记录', minWidth: 130 },
|
||||
{ key: 'status', label: '状态', width: 90, slotName: 'status' },
|
||||
@@ -176,7 +176,7 @@ async function resetPassword(member: TenantMember) {
|
||||
<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="canEditMember" link type="primary" @click="openEdit(row as TenantMember)"><Pencil :size="15" />编辑</el-button><el-button v-if="canResetMemberPassword" link type="primary" @click="resetPassword(row as TenantMember)"><KeyRound :size="15" />重置密码</el-button><el-button v-if="canChangeMemberStatus && !(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="!canEditMember && !canResetMemberPassword && !canChangeMemberStatus" 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 #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>
|
||||
@@ -186,7 +186,7 @@ async function resetPassword(member: TenantMember) {
|
||||
<div v-if="pagedMembers.length" class="mobile-member-list">
|
||||
<article v-for="member in pagedMembers" :key="member.id">
|
||||
<header><div class="member-identity"><span>{{ member.name.slice(0, 1) }}</span><div><strong>{{ member.name }}</strong><small>{{ member.account }}</small></div></div><el-tag :type="member.status === 'ACTIVE' ? 'success' : 'info'" effect="light">{{ member.status === 'ACTIVE' ? '正常' : '已停用' }}</el-tag></header>
|
||||
<dl><div><dt>手机</dt><dd>{{ member.phone }}</dd></div><div><dt>最近登录</dt><dd>{{ member.lastLoginAt }}</dd></div><div class="full"><dt>角色</dt><dd>{{ store.roleNames(member.roleIds).join('、') }}</dd></div></dl>
|
||||
<dl><div><dt>登录手机号</dt><dd>{{ member.phone || '-' }}</dd></div><div><dt>最近登录</dt><dd>{{ member.lastLoginAt }}</dd></div><div class="full"><dt>角色</dt><dd>{{ store.roleNames(member.roleIds).join('、') }}</dd></div></dl>
|
||||
<footer><el-button v-if="canEditMember" link type="primary" @click="openEdit(member)">编辑</el-button><el-button v-if="canResetMemberPassword" link type="primary" @click="resetPassword(member)">重置密码</el-button><el-button v-if="canChangeMemberStatus && !member.isAdministrator" link :type="member.status === 'ACTIVE' ? 'danger' : 'success'" @click="toggleStatus(member)">{{ member.status === 'ACTIVE' ? '停用' : '启用' }}</el-button><span v-if="!canEditMember && !canResetMemberPassword && !canChangeMemberStatus" class="readonly-action">仅查看</span></footer>
|
||||
</article>
|
||||
</div>
|
||||
@@ -199,7 +199,7 @@ async function resetPassword(member: TenantMember) {
|
||||
<el-form-item label="成员姓名" prop="name" required><el-input v-model="form.name" maxlength="64" placeholder="例如 张三" /></el-form-item>
|
||||
<el-form-item label="登录账号" prop="account" required><el-input v-model="form.account" :disabled="Boolean(editingId)" maxlength="64" placeholder="4–64 位字母、数字或 _ . @ -" /></el-form-item>
|
||||
<el-form-item v-if="!editingId" label="初始密码" prop="password" required><el-input v-model="form.password" type="password" show-password autocomplete="new-password" maxlength="64" placeholder="8–64 位,同时包含字母和数字" /></el-form-item>
|
||||
<div class="member-form-grid"><el-form-item label="联系电话" prop="phone"><el-input v-model="form.phone" maxlength="32" placeholder="例如 13800138000(选填)" /></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="form.email" maxlength="128" placeholder="例如 name@example.com(选填)" /></el-form-item></div>
|
||||
<div class="member-form-grid"><el-form-item label="登录手机号" prop="phone"><el-input v-model="form.phone" maxlength="11" inputmode="tel" autocomplete="tel" placeholder="例如 13800138000(选填)" /><span class="member-field-help">用于该成员手机号登录;修改后会同步认证账号。</span></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="form.email" maxlength="128" placeholder="例如 name@example.com(选填)" /></el-form-item></div>
|
||||
<el-form-item label="成员角色" prop="roleIds" required><el-select v-model="form.roleIds" multiple :disabled="Boolean(editingId) && !canGrantMemberRole" placeholder="请选择角色"><el-option v-for="role in store.roles.filter((item) => item.status === 'ACTIVE' && (!item.builtin || form.roleIds.includes(item.id)))" :key="role.id" :label="role.name" :value="role.id" :disabled="role.builtin" /></el-select></el-form-item>
|
||||
<div v-if="!editingId" class="member-form-tip"><KeyRound :size="17" /><span>请通过安全渠道将初始密码交给成员;前端不会保存或再次展示该密码。</span></div>
|
||||
</el-form>
|
||||
@@ -225,6 +225,7 @@ async function resetPassword(member: TenantMember) {
|
||||
.mobile-member-list, .mobile-member-empty { display: none; }
|
||||
.member-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.member-form-tip { display: flex; gap: 10px; padding: 13px; border: 1px solid #cfe1dc; background: #f2f7f6; color: var(--muted); font-size: 11px; line-height: 1.6; }
|
||||
.member-field-help { margin-top: 5px; color: var(--muted); font-size: 10px; line-height: 1.5; }
|
||||
.member-form-tip svg { flex: 0 0 17px; color: var(--primary); }
|
||||
.drawer-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
||||
.members-page :deep(.el-form-item__error) { line-height: 1.45; overflow-wrap: anywhere; }
|
||||
|
||||
Reference in New Issue
Block a user