Files
gao-customer-h5/src/components/CustomerProfileCard.vue
T
2026-08-18 15:15:47 +08:00

139 lines
2.9 KiB
Vue

<template>
<div v-if="customer" class="customer-profile-card">
<div v-if="photoURL" class="avatar-trigger" @click="handlePhotoClick">
<t-image :src="photoURL" :alt="customerName" fit="cover" class="avatar" />
<div @click.stop>
<t-image-viewer
v-model:visible="photoViewerVisible"
:images="photoViewerImages"
show-index
/>
</div>
</div>
<div v-else class="avatar fallback" v-text="avatarText" />
<div class="content">
<div class="header">
<div class="name white">
<span v-text="customerName"></span>
<gender-icon v-if="customer.gender" :value="customer.gender" size="1rem" />
</div>
<t-tag
class="code"
variant="outline"
shape="round"
:content="customerCode"
/>
</div>
<telephone-link class="telephone white" :value="customerTelephone" />
</div>
</div>
</template>
<script setup lang="ts">
import type { Customer } from "@/types/Customer";
import { getCustomerPhotoURL } from "@/utils/customer";
defineOptions({
name: "CustomerProfileCard"
});
const props = defineProps<{
customer?: Customer;
}>();
const photoViewerVisible = ref(false);
const customer = computed(() => props.customer);
const customerName = computed(() => customer.value?.name || customer.value?.code || "");
const avatarText = computed(() => customerName.value.slice(0, 1));
const customerCode = computed(() => customer.value?.code || "");
const customerTelephone = computed(() => customer.value?.telephone || "");
const photoURL = computed(() => customer.value ? getCustomerPhotoURL(customer.value) : "");
const photoViewerImages = computed(() => photoURL.value ? [photoURL.value] : []);
function handlePhotoClick(event: MouseEvent): void {
event.stopPropagation();
openPhotoViewer();
}
function openPhotoViewer(): void {
if (!photoURL.value) {
return;
}
photoViewerVisible.value = true;
}
</script>
<style scoped lang="less">
.customer-profile-card {
gap: .875rem;
padding: .5rem;
display: flex;
box-shadow: var(--td-shadow-1);
background: linear-gradient(135deg, #43cea2, var(--td-brand-color));
border-radius: 1.125rem;
.avatar-trigger {
width: 4rem;
height: 4rem;
border-radius: .875rem;
}
.avatar {
width: 4rem;
height: 4rem;
}
.content {
flex: 1;
display: flex;
min-width: 0;
padding-bottom: .25rem;
flex-direction: column;
justify-content: space-between;
.header {
display: flex;
align-items: center;
padding-right: .5rem;
justify-content: space-between;
.name {
gap: .5rem;
display: flex;
font-size: 1.25rem;
align-items: center;
}
.code {
color: #FFF;
background: transparent;
}
}
.telephone {
display: inline-block;
font-size: .875rem;
}
}
}
:global(html.theme-dark) {
.customer-profile-card {
background: rgba(30, 41, 59, .9);
.content p {
color: #94a3b8;
}
.line h3 {
color: #e2e8f0;
}
.gender {
color: #93c5fd;
}
}
}
</style>