feat: 세력·장수일람 모바일 배치와 근접 미리보기 추가

This commit is contained in:
2026-08-19 17:14:29 +00:00
parent 0c0f0e45a9
commit 1f2ac11978
4 changed files with 502 additions and 37 deletions
@@ -8,9 +8,11 @@ withDefaults(
defineProps<{
generals: GeneralDirectoryGeneral[];
loading?: boolean;
layout?: 'responsive' | 'card';
}>(),
{
loading: false,
layout: 'responsive',
}
);
@@ -18,7 +20,7 @@ const injuredStat = (value: number, injury: number): number => Math.trunc((value
</script>
<template>
<table class="directory-table general-table legacy-bg0">
<table class="directory-table general-table legacy-bg0" :class="{ 'layout-card': layout === 'card' }">
<colgroup>
<col style="width: 64px" />
<col style="width: 140px" />
@@ -124,6 +126,100 @@ const injuredStat = (value: number, injury: number): number => Math.trunc((value
</tr>
</tbody>
</table>
<div class="general-card-list" :class="{ 'layout-card': layout === 'card' }">
<div v-if="loading" class="general-card-loading legacy-bg0">불러오는 중...</div>
<article
v-for="general in generals"
v-else
:key="general.id"
class="general-card legacy-bg0"
:data-general-card-id="general.id"
:data-general-wounded="general.injury"
:data-general-leadership="general.leadership"
:data-general-leadership-bonus="general.leadershipBonus"
:data-general-strength="general.strength"
:data-general-intel="general.intelligence"
:data-is-npc="general.npcState >= 2"
:data-npc-type="general.npcState"
>
<div class="general-card-portrait">
<img
class="general-icon"
width="64"
height="64"
:src="resolveGeneralIconUrl(general)"
alt=""
@error="useDefaultGeneralIcon"
/>
</div>
<div class="general-card-field identity-field name-field">
<span class="field-label">이름</span>
<span :style="{ color: getNpcColor(general.npcState) }">{{ general.name }}</span>
<small v-if="general.ownerName">({{ general.ownerName }})</small>
</div>
<div class="general-card-field identity-field nation-field">
<span class="field-label">국가</span>
<span>{{ general.nationName }}</span>
</div>
<div class="general-card-field">
<span class="field-label">연령</span><span>{{ general.age }}세</span>
</div>
<div class="general-card-field">
<span class="field-label">성격</span>
<span :title="general.personality.info">{{ general.personality.name }}</span>
</div>
<div class="general-card-field">
<span class="field-label">특기</span>
<span :title="`${general.specialDomestic.info} / ${general.specialWar.info}`"
>{{ general.specialDomestic.name }} / {{ general.specialWar.name }}</span
>
</div>
<div class="general-card-field">
<span class="field-label">레벨</span><span>Lv {{ general.experienceLevel }}</span>
</div>
<div class="general-card-field">
<span class="field-label">명성</span><span>{{ general.honorText }}</span>
</div>
<div class="general-card-field">
<span class="field-label">계급</span><span>{{ general.dedicationText }}</span>
</div>
<div class="general-card-field">
<span class="field-label">관직</span>
<span>{{ formatOfficerLevelText(general.officerLevel, general.nationLevel) }}</span>
</div>
<div class="general-card-field">
<span class="field-label">삭턴</span><span>{{ general.killturn }}</span>
</div>
<div class="general-card-field">
<span class="field-label">통솔</span>
<span>
<span :class="{ wounded: general.injury > 0 }">{{
general.injury > 0 ? injuredStat(general.leadership, general.injury) : general.leadership
}}</span
><span v-if="general.leadershipBonus > 0" class="leadership-bonus"
>+{{ general.leadershipBonus }}</span
>
</span>
</div>
<div class="general-card-field">
<span class="field-label">무력</span>
<span :class="{ wounded: general.injury > 0 }">{{
general.injury > 0 ? injuredStat(general.strength, general.injury) : general.strength
}}</span>
</div>
<div class="general-card-field">
<span class="field-label">지력</span>
<span :class="{ wounded: general.injury > 0 }">{{
general.injury > 0 ? injuredStat(general.intelligence, general.injury) : general.intelligence
}}</span>
</div>
<div class="general-card-field penalty-field">
<span class="field-label">벌점</span>
<span>{{ general.refreshScoreTotal }} {{ general.refreshText }}</span>
</div>
</article>
</div>
</template>
<style scoped>
@@ -169,4 +265,91 @@ const injuredStat = (value: number, injury: number): number => Math.trunc((value
height: 64px;
text-align: center;
}
.general-card-list {
display: none;
width: 100%;
}
.general-table.layout-card {
display: none;
}
.general-card-list.layout-card {
display: grid;
}
.general-card,
.general-card-loading {
box-sizing: border-box;
width: 100%;
border: 1px solid gray;
}
.general-card {
display: grid;
grid-template-columns: 66px repeat(4, minmax(0, 1fr));
color: inherit;
font-size: 12px;
line-height: 1.2;
}
.general-card + .general-card {
border-top: 0;
}
.general-card-portrait {
grid-row: 1 / span 4;
display: flex;
align-items: flex-start;
justify-content: center;
border-right: 1px solid gray;
}
.general-card-field {
display: flex;
min-width: 0;
min-height: 29px;
flex-direction: column;
align-items: center;
justify-content: center;
border-right: 1px solid gray;
border-bottom: 1px solid gray;
padding: 2px 3px;
overflow-wrap: anywhere;
text-align: center;
}
.general-card-field:nth-child(3),
.general-card-field:nth-child(7),
.general-card-field:nth-child(11),
.penalty-field {
border-right: 0;
}
.general-card-field:nth-last-child(-n + 4) {
border-bottom: 0;
}
.identity-field {
min-height: 32px;
flex-direction: row;
gap: 4px;
font-size: 14px;
}
.name-field,
.nation-field {
grid-column: span 2;
}
.field-label {
color: #a8d8bd;
font-size: 10px;
line-height: 1;
}
.identity-field .field-label {
font-size: 11px;
}
.general-card-loading {
min-height: 65px;
padding: 22px 8px;
text-align: center;
}
@media (max-width: 600px) {
.general-table:not(.layout-card) {
display: none;
}
.general-card-list {
display: grid;
}
}
</style>