refactor: 用户信息重构(Description)

This commit is contained in:
dap 2025-04-04 20:13:43 +08:00
parent 123f234971
commit 542407dcd6
3 changed files with 108 additions and 146 deletions

View File

@ -66,6 +66,7 @@ export interface User {
roleIds?: string[]; roleIds?: string[];
postIds?: number[]; postIds?: number[];
roleId: string; roleId: string;
deptName: string;
} }
export interface Post { export interface Post {

View File

@ -1,129 +0,0 @@
import type { DescItem } from '#/components/description';
import { DictEnum } from '@vben/constants';
import { Tag } from 'ant-design-vue';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import { renderDict } from '#/utils/render';
dayjs.extend(duration);
dayjs.extend(relativeTime);
function renderTags(list: string[]) {
return (
<div class="flex flex-row flex-wrap gap-0.5">
{list.map((item) => (
<Tag key={item}>{item}</Tag>
))}
</div>
);
}
export const descSchema: DescItem[] = [
{
field: 'userId',
label: '用户ID',
},
{
field: 'status',
label: '用户状态',
render(value) {
return renderDict(value, DictEnum.SYS_NORMAL_DISABLE);
},
},
{
field: 'nickName',
label: '用户信息',
render(_, data) {
const { deptName = '暂无部门信息', nickName, userName } = data;
// 为了兼容新版本和旧版本
let currentDept = deptName;
if (data.dept && data.dept.deptName) {
currentDept = data.dept.deptName;
}
return `${userName} / ${nickName} / ${currentDept}`;
},
},
{
field: 'phonenumber',
label: '手机号',
render(value) {
return value || '未设置手机号码';
},
},
{
field: 'email',
label: '邮箱',
render(value) {
return value || '未设置邮箱地址';
},
},
{
field: 'postNames',
label: '岗位',
render(value) {
if (Array.isArray(value) && value.length === 0) {
return '暂无信息';
}
return renderTags(value);
},
},
{
field: 'roleNames',
label: '权限',
render(value) {
if (Array.isArray(value) && value.length === 0) {
return '暂无信息';
}
return renderTags(value);
},
},
{
field: 'createTime',
label: '创建时间',
},
{
field: 'loginIp',
label: '上次登录IP',
render(value) {
return value || <span class="text-orange-500"></span>;
},
},
{
field: 'loginDate',
label: '上次登录时间',
render(value) {
if (!value) {
return <span class="text-orange-500"></span>;
}
// 默认en显示
dayjs.locale('zh-cn');
// 计算相差秒数
const diffSeconds = dayjs().diff(dayjs(value), 'second');
/**
* (x月 x天)
* https://dayjs.fenxianglu.cn/category/duration.html#%E4%BA%BA%E6%80%A7%E5%8C%96
*
*/
const diffText = dayjs.duration(diffSeconds, 'seconds').humanize();
return (
<div class="flex gap-2">
{value}
<Tag bordered={false} color="cyan">
{diffText}
</Tag>
</div>
);
},
},
{
field: 'remark',
label: '备注',
render(value) {
return value || '无';
},
},
];

View File

@ -1,25 +1,34 @@
<script setup lang="ts"> <script setup lang="ts">
import type { User } from '#/api/system/user/model'; import type { User } from '#/api/system/user/model';
import { computed, shallowRef } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { Descriptions, DescriptionsItem, Tag } from 'ant-design-vue';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import { findUserInfo } from '#/api/system/user'; import { findUserInfo } from '#/api/system/user';
import { Description, useDescription } from '#/components/description'; import { renderDict } from '#/utils/render';
import { descSchema } from './info'; dayjs.extend(duration);
dayjs.extend(relativeTime);
const [BasicModal, modalApi] = useVbenModal({ const [BasicModal, modalApi] = useVbenModal({
onOpenChange: handleOpenChange, onOpenChange: handleOpenChange,
onClosed() {
currentUser.value = null;
},
}); });
const [registerDescription, { setDescProps }] = useDescription({ interface UserWithNames extends User {
column: 1, postNames: string[];
labelStyle: { roleNames: string[];
minWidth: '150px', }
width: '150px', const currentUser = shallowRef<null | UserWithNames>(null);
},
schema: descSchema,
});
async function handleOpenChange(open: boolean) { async function handleOpenChange(open: boolean) {
if (!open) { if (!open) {
@ -41,22 +50,103 @@ async function handleOpenChange(open: boolean) {
.filter((item) => roleIds.includes(item.roleId)) .filter((item) => roleIds.includes(item.roleId))
.map((item) => item.roleName); .map((item) => item.roleName);
interface UserWithNames extends User {
postNames: string[];
roleNames: string[];
}
(user as UserWithNames).postNames = postNames; (user as UserWithNames).postNames = postNames;
(user as UserWithNames).roleNames = roleNames; (user as UserWithNames).roleNames = roleNames;
// //
setDescProps({ data: user }); currentUser.value = user as UserWithNames;
modalApi.modalLoading(false); modalApi.modalLoading(false);
} }
const mixInfo = computed(() => {
if (!currentUser.value) {
return '-';
}
const { deptName, nickName, userName } = currentUser.value;
return `${userName} / ${nickName} / ${deptName ?? '-'}`;
});
const diffLoginTime = computed(() => {
if (!currentUser.value) {
return '-';
}
const { loginDate } = currentUser.value;
// en
dayjs.locale('zh-cn');
//
const diffSeconds = dayjs().diff(dayjs(loginDate), 'second');
/**
* 转为时间显示(x月 x天)
* https://dayjs.fenxianglu.cn/category/duration.html#%E4%BA%BA%E6%80%A7%E5%8C%96
*
*/
const diffText = dayjs.duration(diffSeconds, 'seconds').humanize();
return diffText;
});
</script> </script>
<template> <template>
<BasicModal :footer="false" :fullscreen-button="false" title="用户信息"> <BasicModal :footer="false" :fullscreen-button="false" title="用户信息">
<Description @register="registerDescription" /> <Descriptions v-if="currentUser" size="small" :column="1" bordered>
<DescriptionsItem label="userId">
{{ currentUser.userId }}
</DescriptionsItem>
<DescriptionsItem label="用户状态">
<component
:is="renderDict(currentUser.status, DictEnum.SYS_NORMAL_DISABLE)"
/>
</DescriptionsItem>
<DescriptionsItem label="用户信息">
{{ mixInfo }}
</DescriptionsItem>
<DescriptionsItem label="手机号">
{{ currentUser.phonenumber || '-' }}
</DescriptionsItem>
<DescriptionsItem label="邮箱">
{{ currentUser.email || '-' }}
</DescriptionsItem>
<DescriptionsItem label="岗位">
<div
v-if="currentUser.postNames.length > 0"
class="flex flex-wrap gap-0.5"
>
<Tag v-for="item in currentUser.postNames" :key="item">
{{ item }}
</Tag>
</div>
<span v-else>-</span>
</DescriptionsItem>
<DescriptionsItem label="权限">
<div
v-if="currentUser.roleNames.length > 0"
class="flex flex-wrap gap-0.5"
>
<Tag v-for="item in currentUser.roleNames" :key="item">
{{ item }}
</Tag>
</div>
<span v-else>-</span>
</DescriptionsItem>
<DescriptionsItem label="创建时间">
{{ currentUser.createTime }}
</DescriptionsItem>
<DescriptionsItem label="上次登录IP">
{{ currentUser.loginIp ?? '-' }}
</DescriptionsItem>
<DescriptionsItem label="上次登录时间">
<span>{{ currentUser.loginDate ?? '-' }}</span>
<Tag
class="ml-2"
v-if="diffLoginTime"
:bordered="false"
color="processing"
>
{{ diffLoginTime }}
</Tag>
</DescriptionsItem>
<DescriptionsItem label="备注">
{{ currentUser.remark ?? '-' }}
</DescriptionsItem>
</Descriptions>
</BasicModal> </BasicModal>
</template> </template>