363 lines
9.2 KiB
Vue
363 lines
9.2 KiB
Vue
<script lang="ts" setup>
|
|
import { Icon } from '@iconify/vue';
|
|
|
|
import AppBox from 'components/app/AppBox.vue';
|
|
import AppCircle from 'components/app/AppCircle.vue';
|
|
|
|
defineProps<{
|
|
list: {
|
|
id: string;
|
|
name: string;
|
|
detail?: { label: string; value: string }[];
|
|
male?: boolean;
|
|
female?: boolean;
|
|
disabled?: boolean;
|
|
badge?: string;
|
|
img?: string;
|
|
}[];
|
|
gridColumns?: number;
|
|
noHover?: boolean;
|
|
noAction?: boolean;
|
|
noDetail?: boolean;
|
|
noBg?: boolean;
|
|
detailColumnCount?: number;
|
|
}>();
|
|
|
|
defineEmits<{
|
|
(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;
|
|
}>();
|
|
</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
|
|
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
|
|
flat
|
|
round
|
|
padding="sm"
|
|
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', 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)"
|
|
>
|
|
<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,
|
|
}"
|
|
>
|
|
{{ v.badge }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- detail -->
|
|
<q-separator v-if="!noDetail" />
|
|
<div
|
|
v-if="!noDetail"
|
|
class="q-pt-sm q-px-sm 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 }}
|
|
</span>
|
|
<span>{{ d.value || '-' }}</span>
|
|
</div>
|
|
</div>
|
|
</AppBox>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.person-box {
|
|
background-color: var(--surface-2);
|
|
border-radius: var(--radius-2) !important;
|
|
transition: 100ms ease-in-out;
|
|
padding: 0;
|
|
|
|
&.person-box__disabled {
|
|
opacity: 0.4;
|
|
|
|
.status-circle {
|
|
background-color: var(--surface-1);
|
|
}
|
|
|
|
.locale {
|
|
filter: grayscale();
|
|
}
|
|
}
|
|
|
|
&.person-box__no-detail {
|
|
padding-block: 2rem;
|
|
}
|
|
|
|
&.person-box__no-bg {
|
|
background-color: transparent;
|
|
}
|
|
|
|
& .bg-gender {
|
|
color: hsla(var(--_fg));
|
|
background-color: hsl(var(--_bg));
|
|
|
|
&.bg-gender__disable {
|
|
--_bg: var(--stone-6-hsl);
|
|
background-color: hsla(var(--_bg) / 0.3);
|
|
}
|
|
|
|
&.bg-gender__light {
|
|
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 {
|
|
color: hsla(var(--_fg));
|
|
|
|
&.symbol-gender__disable {
|
|
--_fg: var(--stone-6-hsl);
|
|
}
|
|
|
|
&.symbol-gender__male {
|
|
--_fg: var(--gender-male);
|
|
}
|
|
|
|
&.symbol-gender__female {
|
|
--_fg: var(--gender-female);
|
|
}
|
|
}
|
|
|
|
& .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;
|
|
border-radius: 50%;
|
|
background-color: var(--teal-6);
|
|
border: 2px solid var(--border-color);
|
|
bottom: 0.6rem;
|
|
right: 0.6rem;
|
|
position: absolute;
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
&:not(.person-box__disabled):not(.person-box__no-hover):hover {
|
|
--_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));
|
|
}
|
|
}
|
|
}
|
|
|
|
.person-container {
|
|
display: grid;
|
|
gap: var(--size-6);
|
|
}
|
|
|
|
.avatar {
|
|
block-size: 7rem;
|
|
}
|
|
|
|
.badge {
|
|
display: inline-block;
|
|
border-radius: var(--radius-6);
|
|
background-color: var(--surface-2);
|
|
text-wrap: nowrap;
|
|
}
|
|
</style>
|