From 91bfcf61f44f353dfebc54a57b5b774721de3e9a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 19 十月 2026 09:39:22 +0800
Subject: [PATCH] feat(auth): 更新门户认证页面布局和样式

---
 src/views/_core/profile/modules/profile-user.vue |  158 ++++++++--------------------------------------------
 1 files changed, 25 insertions(+), 133 deletions(-)

diff --git a/src/views/_core/profile/modules/profile-user.vue b/src/views/_core/profile/modules/profile-user.vue
index 57384e3..7eb5073 100644
--- a/src/views/_core/profile/modules/profile-user.vue
+++ b/src/views/_core/profile/modules/profile-user.vue
@@ -1,148 +1,40 @@
 <script setup lang="ts">
 import type { SystemUserProfileApi } from '#/api/system/user/profile';
-
 import { computed } from 'vue';
-
 import { IconifyIcon } from '@vben/icons';
 import { preferences } from '@vben/preferences';
 import { formatDateTime } from '@vben/utils';
-
-import { Descriptions, DescriptionsItem, Tooltip } from 'ant-design-vue';
-
 import { updateUserProfile } from '#/api/system/user/profile';
 import { CropperAvatar } from '#/components/cropper';
 import { useUpload } from '#/components/upload/use-upload';
-
-const props = defineProps<{
-  profile?: SystemUserProfileApi.UserProfileRespVO;
-}>();
-
-const emit = defineEmits<{
-  (e: 'success'): void;
-}>();
-
-const avatar = computed(
-  () => props.profile?.avatar || preferences.app.defaultAvatar,
-);
-
-async function handelUpload({
-  file,
-  filename,
-}: {
-  file: Blob;
-  filename: string;
-}) {
-  // 1. 涓婁紶澶村儚锛岃幏鍙� URL
+const props = defineProps<{ profile: SystemUserProfileApi.UserProfileRespVO }>();
+const emit = defineEmits<{ success: [] }>();
+const avatar = computed(() => props.profile.avatar || preferences.app.defaultAvatar);
+const details = computed(() => [
+  { label: '鐢ㄦ埛璐﹀彿', value: props.profile.username, icon: 'lucide:user-round' },
+  { label: '鎵�灞為儴闂�', value: props.profile.dept?.name, icon: 'lucide:building-2' },
+  { label: '鎵�灞炲矖浣�', value: props.profile.posts?.map(p => p.name).join('銆�'), icon: 'lucide:briefcase-business' },
+  { label: '鎵嬫満鍙风爜', value: props.profile.mobile, icon: 'lucide:smartphone' },
+  { label: '鐢靛瓙閭', value: props.profile.email, icon: 'lucide:mail' },
+]);
+async function handleUpload({ file, filename }: { file: Blob; filename: string }) {
   const { httpRequest } = useUpload();
-  // 灏� Blob 杞崲涓� File
-  const fileObj = new File([file], filename, { type: file.type });
-  const avatar = await httpRequest(fileObj);
-  // 2. 鏇存柊鐢ㄦ埛澶村儚
-  await updateUserProfile({ avatar });
+  const result = await httpRequest(new File([file], filename, { type: file.type }));
+  if (!result.url) throw new Error('澶村儚涓婁紶鏈繑鍥炲浘鐗囧湴鍧�锛岃閲嶈瘯');
+  await updateUserProfile({ avatar: result.url });
 }
 </script>
-
 <template>
-  <div v-if="profile">
-    <div class="flex flex-col items-center">
-      <Tooltip title="鐐瑰嚮涓婁紶澶村儚">
-        <CropperAvatar
-          :show-btn="false"
-          :upload-api="handelUpload"
-          :value="avatar"
-          :width="120"
-          @change="emit('success')"
-        />
-      </Tooltip>
-    </div>
-    <div class="mt-8">
-      <Descriptions :column="2">
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon icon="ant-design:user-outlined" class="mr-1" />
-              鐢ㄦ埛璐﹀彿
-            </div>
-          </template>
-          {{ profile.username }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon
-                icon="ant-design:user-switch-outlined"
-                class="mr-1"
-              />
-              鎵�灞炶鑹�
-            </div>
-          </template>
-          {{ profile.roles.map((role) => role.name).join(',') }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon icon="ant-design:phone-outlined" class="mr-1" />
-              鎵嬫満鍙风爜
-            </div>
-          </template>
-          {{ profile.mobile }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon icon="ant-design:mail-outlined" class="mr-1" />
-              鐢ㄦ埛閭
-            </div>
-          </template>
-          {{ profile.email }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon icon="ant-design:team-outlined" class="mr-1" />
-              鎵�灞為儴闂�
-            </div>
-          </template>
-          {{ profile.dept?.name }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon
-                icon="ant-design:usergroup-add-outlined"
-                class="mr-1"
-              />
-              鎵�灞炲矖浣�
-            </div>
-          </template>
-          {{
-            profile.posts && profile.posts.length > 0
-              ? profile.posts.map((post) => post.name).join(',')
-              : '-'
-          }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon
-                icon="ant-design:clock-circle-outlined"
-                class="mr-1"
-              />
-              鍒涘缓鏃堕棿
-            </div>
-          </template>
-          {{ formatDateTime(profile.createTime) }}
-        </DescriptionsItem>
-        <DescriptionsItem>
-          <template #label>
-            <div class="flex items-center">
-              <IconifyIcon icon="ant-design:login-outlined" class="mr-1" />
-              鐧诲綍鏃堕棿
-            </div>
-          </template>
-          {{ formatDateTime(profile.loginDate) }}
-        </DescriptionsItem>
-      </Descriptions>
-    </div>
+  <div class="profile-user">
+    <div class="profile-user-banner" aria-hidden="true"></div>
+    <div class="profile-avatar"><CropperAvatar :value="avatar" :width="84" :upload-api="handleUpload" :show-btn="true" btn-text="鏇存崲澶村儚" :btn-props="{ type: 'link', size: 'small' }" @change="emit('success')" /></div>
+    <div class="profile-user-name"><h2>{{ profile.nickname || profile.username }}</h2><p>{{ profile.dept?.name || '涓汉璐﹀彿' }}</p><div v-if="profile.roles?.length" class="profile-roles"><span v-for="role in profile.roles" :key="role.id ?? role.name">{{ role.name }}</span></div></div>
+    <dl class="profile-facts"><div v-for="item in details" :key="item.label"><dt><IconifyIcon :icon="item.icon" />{{ item.label }}</dt><dd>{{ item.value || '鏈缃�' }}</dd></div></dl>
+    <div class="profile-login"><span><IconifyIcon icon="lucide:clock-3" />鏈�杩戠櫥褰�</span><time>{{ profile.loginDate ? formatDateTime(profile.loginDate) : '鏆傛棤璁板綍' }}</time><small v-if="profile.createTime">鍔犲叆鏃堕棿锛歿{ formatDateTime(profile.createTime)?.slice(0, 10) }}</small></div>
   </div>
 </template>
+<style scoped>
+.profile-user-banner { height: 78px; background: linear-gradient(100deg, rgb(24 74 116 / 92%), rgb(43 105 154 / 38%)), url('/static/imgs/portal-hero-v2.png') center 60% / cover; }.profile-avatar { position: relative; display: flex; justify-content: center; margin-top: -42px; }.profile-avatar :deep(.group) { border: 4px solid #fff; box-shadow: 0 3px 8px rgb(24 66 106 / 9%); }.profile-avatar :deep(.ant-btn) { font-size: 11px; }.profile-user-name { padding: 5px 20px 20px; text-align: center; }.profile-user-name h2 { margin: 0 0 5px; font-size: 21px; font-weight: 650; overflow-wrap: anywhere; }.profile-user-name p { margin: 0; color: var(--portal-muted); font-size: 12px; }.profile-roles { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: 12px; }.profile-roles span { padding: 3px 8px; border-radius: 4px; color: #2d618b; background: #edf5fc; font-size: 11px; }.profile-facts { margin: 0 20px; padding: 5px 0 10px; border-top: 1px solid #e7eff6; }.profile-facts > div { padding: 12px 0; border-bottom: 1px solid #edf2f7; }.profile-facts dt { display: flex; align-items: center; gap: 7px; margin-bottom: 6px; color: var(--portal-muted); font-size: 11px; }.profile-facts svg,.profile-login svg { width: 14px; height: 14px; }.profile-facts dd { margin: 0; padding-left: 21px; color: var(--portal-ink); font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }.profile-login { display: grid; gap: 6px; padding: 10px 20px 22px; color: var(--portal-muted); font-size: 11px; }.profile-login > span { display: flex; align-items: center; gap: 7px; }.profile-login time { padding-left: 21px; }.profile-login small { padding-left: 21px; font-size: 10px; }
+@media(max-width:767px) { .profile-facts { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); column-gap: 16px; }.profile-login { grid-template-columns: auto 1fr; }.profile-login time { padding: 0; }.profile-login small { grid-column: 1 / -1; } }
+</style>
+

--
Gitblit v1.9.3