mirror of
https://github.com/headporter81/specialsource-homepage-frontend.git
synced 2026-08-08 15:21:11 +09:00
This commit is contained in:
@@ -3,31 +3,45 @@ import { computed } from 'vue';
|
|||||||
|
|
||||||
// [Props 정의]
|
// [Props 정의]
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
progress: { type: Number, required: true, default: 0 },
|
// 현재 진행 값
|
||||||
total: { type: Number, default: 100 },
|
progress: {
|
||||||
status: { type: String, default: 'Initializing...' },
|
type: Number,
|
||||||
barLength: { type: Number, default: 30 },
|
required: true,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 총량
|
||||||
|
total: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
// 하단에 표시할 상태 메시지
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
default: 'Initializing...'
|
||||||
|
},
|
||||||
|
// 막대의 총 글자 길이
|
||||||
|
barLength: {
|
||||||
|
type: Number,
|
||||||
|
default: 30
|
||||||
|
},
|
||||||
|
// 로그인한 유저 ID (Main.vue로부터 유기적 동기화)
|
||||||
username: {
|
username: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'anonymous'
|
default: 'anonymous'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// [핵심 로직: Computed로 실시간 계산]
|
|
||||||
|
|
||||||
// 1. 퍼센트 계산 (0 ~ 100, 소수점 버림)
|
// [핵심 로직: Computed로 실시간 계산]
|
||||||
const percentage = computed(() => {
|
const percentage = computed(() => {
|
||||||
if (props.total <= 0) return 0;
|
if (props.total <= 0) return 0;
|
||||||
const percent = (props.progress / props.total) * 100;
|
const percent = (props.progress / props.total) * 100;
|
||||||
return Math.min(Math.round(percent), 100); // 100%를 넘지 않도록 제한
|
return Math.min(Math.round(percent), 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. ASCII 막대 문자열 생성 (█░ 문자 조합)
|
|
||||||
const barString = computed(() => {
|
const barString = computed(() => {
|
||||||
const filledCount = Math.round((props.barLength * percentage.value) / 100);
|
const filledCount = Math.round((props.barLength * percentage.value) / 100);
|
||||||
const emptyCount = props.barLength - filledCount;
|
const emptyCount = props.barLength - filledCount;
|
||||||
|
|
||||||
// '.repeat()'를 활용해 칼각 유지
|
|
||||||
const filledBar = '█'.repeat(filledCount);
|
const filledBar = '█'.repeat(filledCount);
|
||||||
const emptyBar = '░'.repeat(emptyCount);
|
const emptyBar = '░'.repeat(emptyCount);
|
||||||
|
|
||||||
@@ -57,27 +71,28 @@ const barString = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* [TUI 핵심 스타일 CSS] */
|
/* 🌟 [인프라 패치 핵심]
|
||||||
|
Main.vue의 .homepage-wrapper 하위에서 렌더링되므로
|
||||||
|
상위의 레트로 테마 변수(--tui-...)들을 100% 그대로 상속받아 동적 작동합니다.
|
||||||
|
*/
|
||||||
|
|
||||||
.tui-window {
|
.tui-window {
|
||||||
/* 레트로 터미널 배경색 및 초록색 글씨 */
|
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #33ff33; /* 형광 초록 */
|
/* 🟢 하드코딩된 초록색을 지우고 테마 주축 컬러 변수 바인딩 */
|
||||||
|
color: var(--tui-color);
|
||||||
/* ★가장 중요: 칼각을 위한 고정폭 폰트 설정 */
|
font-family: 'JetBrains Mono', monospace !important;
|
||||||
font-family: 'Courier New', 'Fira Code', 'Monaco', monospace;
|
|
||||||
|
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #33ff33;
|
border: 1px solid var(--tui-color);
|
||||||
box-shadow: 0 0 15px rgba(51, 255, 51, 0.2);
|
box-shadow: 0 0 15px var(--tui-border);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 520px; /* 폰트에 따라 깨지지 않게 최소폭 지정 */
|
min-width: 520px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-header {
|
.tui-header {
|
||||||
color: #aaaaaa; /* 프롬프트는 약간 흐리게 */
|
/* 🟢 서브 텍스트 영역을 테마별 감쇄(Dim) 컬러 변수로 매핑 */
|
||||||
|
color: var(--tui-dim);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
@@ -87,23 +102,20 @@ const barString = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tui-bar-row {
|
.tui-bar-row {
|
||||||
/* 막대가 스르륵 차오르는 느낌을 위한 자간 및 폰트 크기 조정 */
|
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
|
|
||||||
/* █와 ░의 높이를 맞추기 위해 white-space 설정 */
|
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-bracket {
|
.tui-bracket {
|
||||||
color: #888888;
|
color: var(--tui-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-bar {
|
.tui-bar {
|
||||||
/* 막대 문자 자체에 약간의 글로우 효과 */
|
/* 🟢 게이지 블록 자체에 선택한 테마 전용 빛 번짐(Glow) 이펙트 투하 */
|
||||||
text-shadow: 0 0 5px rgba(51, 255, 51, 0.5);
|
text-shadow: 0 0 5px var(--tui-glow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-percent {
|
.tui-percent {
|
||||||
@@ -115,18 +127,28 @@ const barString = computed(() => {
|
|||||||
.tui-status-row {
|
.tui-status-row {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
border-top: 1px dotted #444;
|
border-top: 1px dotted var(--tui-border);
|
||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-label {
|
.tui-label {
|
||||||
color: #888888;
|
color: var(--tui-dim);
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-message {
|
.tui-message {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
/* 메시지가 길어질 경우 대비 */
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 📱 모바일 환경 최적화 대응 스케일 보정 다운사이징 */
|
||||||
|
@media screen and (max-width: 650px) {
|
||||||
|
.tui-window {
|
||||||
|
min-width: 90vw;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.tui-bar-row {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user