feat: update Person Card
This commit is contained in:
parent
e1e7eb4719
commit
4bfe1ccd34
4 changed files with 391 additions and 337 deletions
|
|
@ -5,264 +5,222 @@ import AppBox from 'components/app/AppBox.vue';
|
|||
import AppCircle from 'components/app/AppCircle.vue';
|
||||
|
||||
defineProps<{
|
||||
list: {
|
||||
id: string;
|
||||
data: {
|
||||
name: string;
|
||||
detail?: { label: string; value: string }[];
|
||||
code: string;
|
||||
male?: boolean;
|
||||
female?: boolean;
|
||||
disabled?: boolean;
|
||||
badge?: string;
|
||||
img?: string;
|
||||
}[];
|
||||
gridColumns?: number;
|
||||
detail?: { icon: string; value: string }[];
|
||||
};
|
||||
tag?: [{ color: string; value: string }];
|
||||
disabled?: boolean;
|
||||
noHover?: boolean;
|
||||
noAction?: boolean;
|
||||
noDetail?: boolean;
|
||||
noBg?: boolean;
|
||||
detailColumnCount?: number;
|
||||
canEditProfile?: boolean;
|
||||
history?: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
(e: 'editProfile'): void;
|
||||
(e: 'history', id: string): void;
|
||||
(e: 'deleteCard', id: string): void;
|
||||
(
|
||||
e: 'updateCard',
|
||||
action: 'FORM' | 'INFO',
|
||||
id: string,
|
||||
isEdit?: boolean,
|
||||
): void;
|
||||
(e: 'enterCard', action: 'FORM' | 'INFO', id: string): void;
|
||||
(e: 'toggleStatus', id: string, status: boolean): void;
|
||||
(e: 'history'): void;
|
||||
(e: 'deleteCard'): void;
|
||||
(e: 'updateCard', action: 'FORM' | 'INFO', isEdit?: boolean): void;
|
||||
(e: 'enterCard', action: 'FORM' | 'INFO'): void;
|
||||
(e: 'toggleStatus', status: boolean): void;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="person-container"
|
||||
:style="`grid-template-columns: repeat( ${
|
||||
gridColumns
|
||||
? `${gridColumns}, 1fr`
|
||||
: $q.screen.gt.sm
|
||||
? '4, 1fr'
|
||||
: $q.screen.gt.xs
|
||||
? '2, 1fr'
|
||||
: '1, 1fr'
|
||||
})`"
|
||||
<AppBox
|
||||
bordered
|
||||
no-padding
|
||||
class="column person-box"
|
||||
:class="{
|
||||
'person-box__disabled': disabled,
|
||||
'person-box__no-hover': noHover,
|
||||
'person-box__no-detail': !data.detail,
|
||||
'person-box__no-bg': noBg,
|
||||
}"
|
||||
@click="$emit('enterCard', 'INFO')"
|
||||
>
|
||||
<AppBox
|
||||
bordered
|
||||
class="column person-box"
|
||||
:class="{
|
||||
'person-box__disabled': v.disabled,
|
||||
'person-box__no-hover': noHover,
|
||||
'person-box__no-detail': noDetail,
|
||||
'person-box__no-bg': noBg,
|
||||
}"
|
||||
@click="$emit('enterCard', 'INFO', v.id)"
|
||||
v-for="(v, i) in list"
|
||||
:key="i"
|
||||
>
|
||||
<div class="q-pa-sm column items-center">
|
||||
<!-- kebab menu -->
|
||||
<div class="full-width text-right" v-if="!noAction">
|
||||
<q-btn
|
||||
v-if="history"
|
||||
flat
|
||||
round
|
||||
class="app-text-muted-2"
|
||||
padding="xs"
|
||||
icon="mdi-history"
|
||||
size="sm"
|
||||
@click.stop="$emit('history', v.id)"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
padding="xs"
|
||||
class="app-text-muted-2"
|
||||
icon="mdi-dots-vertical"
|
||||
size="sm"
|
||||
@click.stop
|
||||
<div class="column items-center">
|
||||
<!-- kebab menu -->
|
||||
<div class="full-width flex" v-if="!noAction">
|
||||
<div style="margin-right: auto">
|
||||
<span
|
||||
class="tags"
|
||||
v-for="v in tag"
|
||||
:class="{ [`tags__${v.color}`]: true }"
|
||||
>
|
||||
<q-menu class="bordered">
|
||||
<q-list v-close-popup>
|
||||
<q-item
|
||||
clickable
|
||||
dense
|
||||
class="row q-py-sm"
|
||||
style="white-space: nowrap"
|
||||
@click="$emit('enterCard', 'INFO', v.id)"
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-eye-outline"
|
||||
class="col-3"
|
||||
size="xs"
|
||||
style="color: hsl(var(--green-6-hsl))"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('viewDetail') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item
|
||||
dense
|
||||
clickable
|
||||
class="row q-py-sm"
|
||||
style="white-space: nowrap"
|
||||
@click="$emit('updateCard', 'INFO', v.id, true)"
|
||||
v-close-popup
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-pencil-outline"
|
||||
class="col-3"
|
||||
size="xs"
|
||||
style="color: hsl(var(--cyan-6-hsl))"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('edit') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item
|
||||
dense
|
||||
clickable
|
||||
@click="$emit('deleteCard', v.id)"
|
||||
v-close-popup
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-trash-can-outline"
|
||||
size="xs"
|
||||
class="col-3 app-text-negative"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('delete') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item dense>
|
||||
<q-item-section class="q-py-sm">
|
||||
<div class="q-pa-sm surface-2 rounded">
|
||||
<q-toggle
|
||||
dense
|
||||
size="sm"
|
||||
@click="
|
||||
$emit('toggleStatus', v.id, v.disabled === false)
|
||||
"
|
||||
:model-value="!v.disabled"
|
||||
val="xs"
|
||||
padding="none"
|
||||
>
|
||||
<div class="q-ml-xs">
|
||||
{{
|
||||
!v.disabled
|
||||
? $t('switchOnLabel')
|
||||
: $t('switchOffLabel')
|
||||
}}
|
||||
</div>
|
||||
</q-toggle>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!-- profile -->
|
||||
<div style="position: relative">
|
||||
<AppCircle
|
||||
bordered
|
||||
class="avatar"
|
||||
style="border: 2px solid var(--border-color)"
|
||||
:class="{ 'edit-profile': canEditProfile }"
|
||||
@click="
|
||||
() => {
|
||||
if (canEditProfile) $emit('editProfile');
|
||||
}
|
||||
"
|
||||
>
|
||||
<q-img
|
||||
:src="v.img"
|
||||
style="object-fit: cover; width: 100%; height: 100%"
|
||||
/>
|
||||
<div class="status-circle">
|
||||
<q-icon
|
||||
:name="`mdi-${v.disabled ? 'close' : 'check'}`"
|
||||
:style="`color:${v.disabled ? 'var(--gray-6)' : 'white'}`"
|
||||
/>
|
||||
</div>
|
||||
</AppCircle>
|
||||
</div>
|
||||
|
||||
<!-- name symbol -->
|
||||
<span class="items-center row q-my-sm">
|
||||
{{ v.name }}
|
||||
<Icon
|
||||
class="q-pl-sm"
|
||||
:class="{
|
||||
'symbol-gender': v.male || v.female,
|
||||
'symbol-gender__male': !v.disabled && v.male,
|
||||
'symbol-gender__female': !v.disabled && v.female,
|
||||
'symbol-gender__disable': v.disabled,
|
||||
}"
|
||||
:icon="`material-symbols:${v.male ? 'male' : 'female'}`"
|
||||
width="24px"
|
||||
/>
|
||||
<!-- <Icon class="locale q-pl-sm" icon="circle-flags:th" width="24px" /> -->
|
||||
</span>
|
||||
<span
|
||||
v-if="v.badge"
|
||||
class="badge q-px-sm"
|
||||
:class="{
|
||||
'bg-gender': v.male || v.female,
|
||||
'bg-gender__male': !v.disabled && v.male,
|
||||
'bg-gender__female': !v.disabled && v.female,
|
||||
'bg-gender__disable': v.disabled,
|
||||
empty: !v.male && !v.female,
|
||||
}"
|
||||
>
|
||||
{{ v.badge }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- detail -->
|
||||
<q-separator v-if="!noDetail" />
|
||||
<div
|
||||
v-if="!noDetail"
|
||||
class="q-pt-sm q-px-md q-pb-md person-detail rounded-b full-width"
|
||||
:class="{
|
||||
'bg-gender': v.male || v.female,
|
||||
'bg-gender__light':
|
||||
(!v.disabled && v.male) || (!v.disabled && v.female),
|
||||
'bg-gender__male': !v.disabled && v.male,
|
||||
'bg-gender__female': !v.disabled && v.female,
|
||||
'bg-gender__disable': v.disabled,
|
||||
}"
|
||||
:style="
|
||||
(detailColumnCount &&
|
||||
`grid-template-columns: repeat(${detailColumnCount}, 1fr);`) ||
|
||||
''
|
||||
"
|
||||
>
|
||||
<div v-for="(d, j) in v.detail" :key="j">
|
||||
<span class="app-text-muted-2">
|
||||
{{ d.label }}
|
||||
{{ v.value }}
|
||||
</span>
|
||||
<span>{{ d.value || '-' }}</span>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="history"
|
||||
flat
|
||||
round
|
||||
class="app-text-muted-2"
|
||||
padding="xs"
|
||||
icon="mdi-history"
|
||||
size="sm"
|
||||
@click.stop="$emit('history')"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
padding="xs"
|
||||
class="app-text-muted-2"
|
||||
icon="mdi-dots-vertical"
|
||||
size="sm"
|
||||
@click.stop
|
||||
>
|
||||
<q-menu class="bordered">
|
||||
<q-list v-close-popup>
|
||||
<q-item
|
||||
clickable
|
||||
dense
|
||||
class="row q-py-sm"
|
||||
style="white-space: nowrap"
|
||||
@click="$emit('enterCard', 'INFO')"
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-eye-outline"
|
||||
class="col-3"
|
||||
size="xs"
|
||||
style="color: hsl(var(--green-6-hsl))"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('viewDetail') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item
|
||||
dense
|
||||
clickable
|
||||
class="row q-py-sm"
|
||||
style="white-space: nowrap"
|
||||
@click="$emit('updateCard', 'INFO', true)"
|
||||
v-close-popup
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-pencil-outline"
|
||||
class="col-3"
|
||||
size="xs"
|
||||
style="color: hsl(var(--cyan-6-hsl))"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('edit') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item
|
||||
dense
|
||||
clickable
|
||||
@click="$emit('deleteCard')"
|
||||
v-close-popup
|
||||
>
|
||||
<q-icon
|
||||
name="mdi-trash-can-outline"
|
||||
size="xs"
|
||||
class="col-3 app-text-negative"
|
||||
/>
|
||||
<span class="col-9 q-px-md flex items-center">
|
||||
{{ $t('delete') }}
|
||||
</span>
|
||||
</q-item>
|
||||
<q-item dense>
|
||||
<q-item-section class="q-py-sm">
|
||||
<div class="q-pa-sm surface-2 rounded">
|
||||
<q-toggle
|
||||
dense
|
||||
size="sm"
|
||||
@click="$emit('toggleStatus', disabled === false)"
|
||||
:model-value="!disabled"
|
||||
val="xs"
|
||||
padding="none"
|
||||
>
|
||||
<div class="q-ml-xs">
|
||||
{{
|
||||
!disabled ? $t('switchOnLabel') : $t('switchOffLabel')
|
||||
}}
|
||||
</div>
|
||||
</q-toggle>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!-- profile -->
|
||||
<AppCircle
|
||||
bordered
|
||||
class="avatar"
|
||||
style="border: 2px solid var(--border-color); overflow: visible"
|
||||
@click="$emit('editProfile')"
|
||||
>
|
||||
<q-img
|
||||
:src="data.img"
|
||||
style="
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
"
|
||||
/>
|
||||
<div class="avatar__status"></div>
|
||||
</AppCircle>
|
||||
|
||||
<!-- name symbol -->
|
||||
<span class="items-center row">
|
||||
{{ data.name }}
|
||||
<Icon
|
||||
class="q-pl-xs"
|
||||
:class="{
|
||||
'symbol-gender': data.male || data.female,
|
||||
'symbol-gender__male': !disabled && data.male,
|
||||
'symbol-gender__female': !disabled && data.female,
|
||||
'symbol-gender__disable': disabled,
|
||||
}"
|
||||
:icon="`material-symbols:${data.male ? 'male' : 'female'}`"
|
||||
width="24px"
|
||||
/>
|
||||
</span>
|
||||
<span style="color: hsl(var(--text-mute)); scale: 0.9">
|
||||
{{ data.code || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- detail -->
|
||||
<q-separator v-if="data.detail" class="q-my-sm q-mx-md" />
|
||||
|
||||
<div class="row" v-for="v in data.detail" :key="v.value" v-if="data.detail">
|
||||
<div class="column items-center" style="width: min-content">
|
||||
<div class="detail-icon">
|
||||
<q-icon size="md" style="scale: 0.5" :name="v.icon" />
|
||||
</div>
|
||||
</div>
|
||||
</AppBox>
|
||||
</div>
|
||||
<div class="col row items-center full-width">
|
||||
<span class="ellipsis">{{ v.value || '-' }}</span>
|
||||
<q-tooltip
|
||||
anchor="bottom left"
|
||||
self="bottom left"
|
||||
:offset="[10, 20]"
|
||||
:delay="500"
|
||||
>
|
||||
{{ v.value || '-' }}
|
||||
</q-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</AppBox>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.person-box {
|
||||
background-color: var(--surface-2);
|
||||
border-radius: var(--radius-2) !important;
|
||||
background-color: var(--surface-1);
|
||||
transition: 100ms ease-in-out;
|
||||
padding: 0;
|
||||
padding: var(--size-2);
|
||||
|
||||
&.person-box__disabled {
|
||||
opacity: 0.4;
|
||||
|
|
@ -284,6 +242,13 @@ defineEmits<{
|
|||
background-color: transparent;
|
||||
}
|
||||
|
||||
& .detail-icon {
|
||||
color: hsl(var(--text-mute-2));
|
||||
background-color: hsla(var(--stone-6-hsl) / 0.15);
|
||||
border-radius: 50%;
|
||||
scale: 0.8;
|
||||
}
|
||||
|
||||
& .bg-gender {
|
||||
color: hsla(var(--_fg));
|
||||
background-color: hsl(var(--_bg));
|
||||
|
|
@ -297,16 +262,6 @@ defineEmits<{
|
|||
color: unset;
|
||||
background-color: hsla(var(--_bg) / 0.1);
|
||||
}
|
||||
|
||||
&.bg-gender__male {
|
||||
--_fg: 0 100 100%;
|
||||
--_bg: var(--gender-male);
|
||||
}
|
||||
|
||||
&.bg-gender__female {
|
||||
--_fg: 0 100 100%;
|
||||
--_bg: var(--gender-female);
|
||||
}
|
||||
}
|
||||
|
||||
& .symbol-gender {
|
||||
|
|
@ -325,23 +280,6 @@ defineEmits<{
|
|||
}
|
||||
}
|
||||
|
||||
& .person-detail {
|
||||
display: grid;
|
||||
flex-grow: 1;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--size-2);
|
||||
overflow-x: scroll;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > :first-child {
|
||||
font-size: var(--font-size-0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .status-circle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
|
@ -360,11 +298,6 @@ defineEmits<{
|
|||
--_hover: hsl(var(--gender-male));
|
||||
cursor: pointer;
|
||||
box-shadow: var(--shadow-2);
|
||||
|
||||
& .person-detail {
|
||||
--_hover: hsl(var(--_bg));
|
||||
box-shadow: inset 0em -5px hsl(var(--_bg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,17 +307,25 @@ defineEmits<{
|
|||
}
|
||||
|
||||
.avatar {
|
||||
block-size: 7rem;
|
||||
}
|
||||
block-size: 5rem;
|
||||
transform: rotate(45deg);
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
border-radius: var(--radius-6);
|
||||
background-color: var(--surface-2);
|
||||
text-wrap: nowrap;
|
||||
& .avatar__status {
|
||||
content: ' ';
|
||||
display: block;
|
||||
block-size: 1rem;
|
||||
aspect-ratio: 1;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
right: -0.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
top: calc(50% - 0.5rem);
|
||||
bottom: calc(50% - 0.5rem);
|
||||
background-color: hsla(var(--positive-bg) / 1);
|
||||
}
|
||||
|
||||
&.empty {
|
||||
background-color: var(--surface-tab);
|
||||
& :deep(.q-img) {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -396,4 +337,60 @@ defineEmits<{
|
|||
opacity: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: inline-block;
|
||||
color: hsla(var(--_color) / 1);
|
||||
background: hsla(var(--_color) / 0.15);
|
||||
border-radius: var(--radius-2);
|
||||
padding-inline: var(--size-2);
|
||||
}
|
||||
|
||||
.tags__purple {
|
||||
--_color: var(--violet-11-hsl);
|
||||
}
|
||||
|
||||
.tags__pink {
|
||||
--_color: var(--pink-6-hsl);
|
||||
}
|
||||
|
||||
.tags__green {
|
||||
--_color: var(--teal-10-hsl);
|
||||
}
|
||||
|
||||
.tags__cyan {
|
||||
--_color: var(--cyan-7-hsl);
|
||||
}
|
||||
|
||||
.tags__red {
|
||||
--_color: var(--red-6-hsl);
|
||||
}
|
||||
|
||||
.tags__yellow {
|
||||
--_color: var(--orange-4-hsl);
|
||||
}
|
||||
|
||||
.tags__orange {
|
||||
--_color: var(--orange-5-hsl);
|
||||
}
|
||||
|
||||
.tags__magenta {
|
||||
--_color: var(--pink-8-hsl);
|
||||
}
|
||||
|
||||
.dark .tags__purple {
|
||||
--_color: var(--violet-10-hsl);
|
||||
}
|
||||
|
||||
.dark .tags__green {
|
||||
--_color: var(--teal-8-hsl);
|
||||
}
|
||||
|
||||
.dark .tags__orange {
|
||||
--_color: var(--orange-6-hsl);
|
||||
}
|
||||
|
||||
.dark .tags__magenta {
|
||||
--_color: var(--pink-7-hsl);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue