feat: 标准资源使用独立页面并优化详情布局
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
<!--
|
||||
功能:展示账户类资源的头像、用户名、唯一标识和创建时间摘要。
|
||||
版本:v1.0.0
|
||||
-->
|
||||
<template>
|
||||
<a-card class="account-card" :bordered="false">
|
||||
<div class="account-summary">
|
||||
<div class="avatar-column">
|
||||
<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;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user