2026-08-11 00:49:41 +08:00
|
|
|
|
<!--
|
|
|
|
|
|
功能:展示账户类资源的头像、用户名、唯一标识和创建时间摘要。
|
2026-08-11 02:01:00 +08:00
|
|
|
|
版本:v1.1.0
|
2026-08-11 00:49:41 +08:00
|
|
|
|
-->
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<a-card class="account-card" :bordered="false">
|
2026-08-11 02:01:00 +08:00
|
|
|
|
<div class="account-summary" :class="{ 'account-summary-text': !avatarEnabled }">
|
|
|
|
|
|
<div v-if="avatarEnabled" class="avatar-column">
|
2026-08-11 00:49:41 +08:00
|
|
|
|
<button
|
|
|
|
|
|
class="avatar-button"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
:disabled="mode === 'detail' || !avatarEnabled"
|
|
|
|
|
|
:aria-label="mode === 'detail' || !avatarEnabled ? '账户头像' : '选择账户头像'"
|
|
|
|
|
|
@click="chooseAvatar"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-avatar :size="120" class="account-avatar">
|
|
|
|
|
|
<img :src="avatarUrl" alt="账户头像" />
|
|
|
|
|
|
</a-avatar>
|
|
|
|
|
|
<span v-if="mode !== 'detail' && avatarEnabled" class="camera-badge"><icon-camera /></span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<input
|
|
|
|
|
|
ref="avatarInput"
|
|
|
|
|
|
class="avatar-input"
|
|
|
|
|
|
type="file"
|
|
|
|
|
|
accept="image/jpeg,image/png"
|
|
|
|
|
|
@change="onAvatarSelected"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span v-if="mode !== 'detail' && avatarEnabled" class="avatar-help">JPG/PNG,最大 2 MB</span>
|
|
|
|
|
|
<a-button v-if="mode !== 'detail' && avatarEnabled && canClear" type="text" size="mini" @click="emit('clear-avatar')">
|
|
|
|
|
|
恢复默认头像
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="identity-summary">
|
|
|
|
|
|
<h2>{{ summaryTitle }}</h2>
|
|
|
|
|
|
<dl>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<dt>用户名:</dt>
|
|
|
|
|
|
<dd>{{ String(record.username ?? '保存后确定') }}</dd>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<dt>唯一标识:</dt>
|
|
|
|
|
|
<dd class="identity-value">{{ String(record.identity ?? '保存后生成') }}</dd>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<dt>创建时间:</dt>
|
|
|
|
|
|
<dd>{{ displayRawValue('created_at', record.created_at) }}</dd>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</dl>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, ref } from 'vue';
|
|
|
|
|
|
import { IconCamera } from '@arco-design/web-vue/es/icon';
|
|
|
|
|
|
import { displayRawValue } from '@/api/resource-display';
|
|
|
|
|
|
import type { ResourceRow, RecordPageMode } from '@/api/resource-page-rules';
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
mode: RecordPageMode;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
record: ResourceRow;
|
|
|
|
|
|
avatarUrl: string;
|
|
|
|
|
|
avatarEnabled: boolean;
|
|
|
|
|
|
canClear: boolean;
|
|
|
|
|
|
}>();
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
'select-avatar': [file: File];
|
|
|
|
|
|
'clear-avatar': [];
|
|
|
|
|
|
}>();
|
|
|
|
|
|
const avatarInput = ref<HTMLInputElement>();
|
|
|
|
|
|
const summaryTitle = computed(() =>
|
|
|
|
|
|
String(
|
|
|
|
|
|
props.record.name ??
|
|
|
|
|
|
props.record.display_name ??
|
|
|
|
|
|
props.record.real_name ??
|
|
|
|
|
|
props.record.username ??
|
|
|
|
|
|
(props.mode === 'create' ? `新建${props.title}` : props.title),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
function chooseAvatar() {
|
|
|
|
|
|
if (props.mode !== 'detail' && props.avatarEnabled)
|
|
|
|
|
|
avatarInput.value?.click();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function onAvatarSelected(event: Event) {
|
|
|
|
|
|
const input = event.target as HTMLInputElement;
|
|
|
|
|
|
const file = input.files?.[0];
|
|
|
|
|
|
if (file) emit('select-avatar', file);
|
|
|
|
|
|
input.value = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="less">
|
|
|
|
|
|
.account-card {
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.account-summary {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 180px minmax(360px, 500px);
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 44px;
|
|
|
|
|
|
min-height: 168px;
|
|
|
|
|
|
padding: 18px 32px;
|
|
|
|
|
|
}
|
2026-08-11 02:01:00 +08:00
|
|
|
|
.account-summary-text {
|
|
|
|
|
|
grid-template-columns: minmax(420px, 720px);
|
|
|
|
|
|
justify-content: start;
|
|
|
|
|
|
min-height: 116px;
|
|
|
|
|
|
padding: 14px 32px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.account-summary-text .identity-summary {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
2026-08-11 00:49:41 +08:00
|
|
|
|
.avatar-column {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.avatar-button {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.avatar-button:disabled {
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
.account-avatar {
|
|
|
|
|
|
background: var(--color-fill-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
.account-avatar img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
}
|
|
|
|
|
|
.camera-badge {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 2px;
|
|
|
|
|
|
bottom: 2px;
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
width: 34px;
|
|
|
|
|
|
height: 34px;
|
|
|
|
|
|
place-items: center;
|
|
|
|
|
|
color: rgb(var(--primary-6));
|
|
|
|
|
|
background: var(--color-bg-2);
|
|
|
|
|
|
border: 3px solid var(--color-bg-2);
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
|
|
|
|
|
|
}
|
|
|
|
|
|
.avatar-input {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.avatar-help {
|
|
|
|
|
|
color: var(--color-text-3);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary h2 {
|
|
|
|
|
|
margin: 0 0 14px;
|
|
|
|
|
|
color: var(--color-text-1);
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary dl,
|
|
|
|
|
|
.identity-summary dd {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary dl > div {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 92px minmax(0, 1fr);
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
min-height: 38px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary dt {
|
|
|
|
|
|
color: var(--color-text-3);
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary dd {
|
|
|
|
|
|
color: var(--color-text-1);
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-value {
|
|
|
|
|
|
color: rgb(var(--primary-6)) !important;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
|
|
.account-summary {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
padding: 20px 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.identity-summary {
|
|
|
|
|
|
width: min(100%, 440px);
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
}
|
2026-08-11 02:01:00 +08:00
|
|
|
|
.account-summary-text {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
|
min-height: auto;
|
|
|
|
|
|
padding: 14px 8px;
|
|
|
|
|
|
}
|
2026-08-11 00:49:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|