feat: 네이티브 alert/confirm/prompt → 공용 인앱 다이얼로그 (PC 'sb_pt' 제거)
CI / build (push) Failing after 15m30s
CI / build (push) Failing after 15m30s
- composables/dialog + components/ui/AppDialog(App 루트 마운트) - window.alert 전역 오버라이드로 모든 alert 인앱화 - confirm/prompt(동기)는 각 호출부를 await dialog.confirm/prompt 로 교체 (가계부·예산·고정지출·계좌·태그·관리자·게시판·설정 — 삭제/열람제한 등) - 웹/PC/APK 일관 UI, 의미있는 제목 부여 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<script setup>
|
||||
import { nextTick, watch, ref } from 'vue'
|
||||
import { dialogState, dialogConfirm, dialogCancel } from '@/composables/dialog'
|
||||
|
||||
const inputEl = ref(null)
|
||||
// prompt 열릴 때 입력칸 포커스
|
||||
watch(
|
||||
() => dialogState.open,
|
||||
async (open) => {
|
||||
if (open && dialogState.type === 'prompt') {
|
||||
await nextTick()
|
||||
inputEl.value?.focus()
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="dlg-fade">
|
||||
<div v-if="dialogState.open" class="dlg-backdrop" @click.self="dialogCancel">
|
||||
<div class="dlg" role="dialog" aria-modal="true">
|
||||
<h2 class="dlg-title">{{ dialogState.title }}</h2>
|
||||
<p v-if="dialogState.message" class="dlg-msg">{{ dialogState.message }}</p>
|
||||
<input
|
||||
v-if="dialogState.type === 'prompt'"
|
||||
ref="inputEl"
|
||||
v-model="dialogState.inputValue"
|
||||
class="dlg-input"
|
||||
:placeholder="dialogState.placeholder"
|
||||
@keyup.enter="dialogConfirm"
|
||||
/>
|
||||
<div class="dlg-buttons">
|
||||
<button v-if="dialogState.type !== 'alert'" type="button" class="dlg-btn" @click="dialogCancel">
|
||||
{{ dialogState.cancelText }}
|
||||
</button>
|
||||
<button type="button" class="dlg-btn primary" :class="{ danger: dialogState.danger }" @click="dialogConfirm">
|
||||
{{ dialogState.okText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dlg-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 1rem;
|
||||
}
|
||||
.dlg {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 340px;
|
||||
padding: 1.4rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
|
||||
background: var(--color-background);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.dlg-title {
|
||||
font-size: 1.05rem;
|
||||
margin: 0 0 0.6rem;
|
||||
}
|
||||
.dlg-msg {
|
||||
margin: 0 0 1rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.55;
|
||||
white-space: pre-line;
|
||||
}
|
||||
.dlg-input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem 0.7rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
background: var(--color-background-soft);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
}
|
||||
.dlg-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.dlg-btn {
|
||||
padding: 0.45rem 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
background: var(--color-background-soft);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dlg-btn.primary {
|
||||
background: hsla(160, 100%, 37%, 1);
|
||||
border-color: hsla(160, 100%, 37%, 1);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.dlg-btn.primary.danger {
|
||||
background: #c0392b;
|
||||
border-color: #c0392b;
|
||||
}
|
||||
.dlg-fade-enter-active,
|
||||
.dlg-fade-leave-active {
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
.dlg-fade-enter-from,
|
||||
.dlg-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user