init project

This commit is contained in:
Timi
2026-08-01 18:01:23 +08:00
parent 286fa1ba56
commit 11c48bf00c
107 changed files with 19350 additions and 1 deletions
+130
View File
@@ -0,0 +1,130 @@
<template>
<div class="customer-profile-card">
<t-button v-if="photoURL" variant="text" class="avatar-trigger" @click="openPhotoViewer">
<t-image :src="photoURL" :alt="customerName" fit="cover" class="avatar" />
</t-button>
<div v-else class="avatar fallback" v-text="avatarText" />
<div class="content">
<div class="header">
<div class="name">
<span v-text="customerName"></span>
<gender-icon v-if="customer?.gender" :value="customer.gender" size="1rem" />
</div>
<span class="gray" v-text="customerCode" />
</div>
<span class="telephone gray" v-text="customerTelephone" />
</div>
<t-image-viewer
v-model:visible="photoViewerVisible"
:images="photoViewerImages"
show-index
/>
</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 openPhotoViewer(): void {
if (!photoURL.value) {
return;
}
photoViewerVisible.value = true;
}
</script>
<style scoped lang="less">
.customer-profile-card {
gap: .875rem;
padding: .5rem;
display: flex;
background: #e6eeff;
border-radius: 1.125rem;
.avatar-trigger {
padding: 0;
border: 0;
width: 4rem;
height: 4rem;
display: block;
background: transparent;
border-radius: .875rem;
}
.avatar {
width: 4rem;
height: 4rem;
display: block;
border-radius: .875rem;
&.fallback {
color: #2563eb;
display: flex;
font-size: 1.5rem;
background: #dbeafe;
align-items: center;
font-weight: 600;
justify-content: center;
}
}
.content {
flex: 1;
min-width: 0;
.header {
display: flex;
align-items: center;
padding-right: .5rem;
justify-content: space-between;
.name {
gap: .5rem;
display: flex;
align-items: center;
}
}
.telephone {
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>