- wtm-frontend → wtm-frontend-vue 이름 변경 - wtm-frontend-react 추가 (React 18 + PrimeReact + Zustand) - 동일한 모듈 구조 및 API 연동 (Vue 버전과 기능 동일) - Vue:5173 / React:5174 포트 분리 - 개발자 가이드에 React 프론트엔드 안내 추가 - .gitignore: Claude/OMC, 문서 생성 스크립트, package-lock 제외 - 불필요 파일 git 추적 제거 (.omc, generate_*.py, regenerate_*.py) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
134 줄
3.5 KiB
Vue
134 줄
3.5 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import Password from 'primevue/password';
|
|
import Button from 'primevue/button';
|
|
import Card from 'primevue/card';
|
|
import Message from 'primevue/message';
|
|
import { authApi } from '../auth.service';
|
|
|
|
const router = useRouter();
|
|
|
|
const currentPassword = ref('');
|
|
const newPassword = ref('');
|
|
const confirmPassword = ref('');
|
|
const error = ref('');
|
|
const success = ref('');
|
|
const loading = ref(false);
|
|
|
|
async function onSubmit() {
|
|
error.value = '';
|
|
success.value = '';
|
|
|
|
if (newPassword.value !== confirmPassword.value) {
|
|
error.value = '새 비밀번호가 일치하지 않습니다.';
|
|
return;
|
|
}
|
|
|
|
if (newPassword.value.length < 8) {
|
|
error.value = '비밀번호는 최소 8자 이상이어야 합니다.';
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
try {
|
|
await authApi.changePassword({
|
|
currentPassword: currentPassword.value,
|
|
newPassword: newPassword.value,
|
|
});
|
|
success.value = '비밀번호가 변경되었습니다.';
|
|
currentPassword.value = '';
|
|
newPassword.value = '';
|
|
confirmPassword.value = '';
|
|
setTimeout(() => router.push('/dashboard'), 1500);
|
|
} catch (e: any) {
|
|
error.value = e?.response?.data?.detail ?? '비밀번호 변경에 실패했습니다.';
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="change-password-page">
|
|
<Card class="change-password-page__card">
|
|
<template #title>비밀번호 변경</template>
|
|
<template #content>
|
|
<Message v-if="error" severity="error" :closable="false" style="width: 100%; margin-bottom: 1rem;">
|
|
{{ error }}
|
|
</Message>
|
|
<Message v-if="success" severity="success" :closable="false" style="width: 100%; margin-bottom: 1rem;">
|
|
{{ success }}
|
|
</Message>
|
|
|
|
<form class="change-password-page__form" @submit.prevent="onSubmit">
|
|
<div class="form-field">
|
|
<label class="form-field__label">현재 비밀번호</label>
|
|
<Password
|
|
v-model="currentPassword"
|
|
placeholder="현재 비밀번호 입력"
|
|
:feedback="false"
|
|
toggleMask
|
|
fluid
|
|
:inputStyle="{ width: '100%' }"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-field">
|
|
<label class="form-field__label">새 비밀번호</label>
|
|
<Password
|
|
v-model="newPassword"
|
|
placeholder="새 비밀번호 입력"
|
|
toggleMask
|
|
fluid
|
|
:inputStyle="{ width: '100%' }"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-field">
|
|
<label class="form-field__label">새 비밀번호 확인</label>
|
|
<Password
|
|
v-model="confirmPassword"
|
|
placeholder="새 비밀번호 다시 입력"
|
|
:feedback="false"
|
|
toggleMask
|
|
fluid
|
|
:inputStyle="{ width: '100%' }"
|
|
/>
|
|
</div>
|
|
|
|
<Button
|
|
type="submit"
|
|
label="비밀번호 변경"
|
|
icon="pi pi-check"
|
|
:loading="loading"
|
|
fluid
|
|
style="margin-top: 0.5rem;"
|
|
/>
|
|
</form>
|
|
</template>
|
|
</Card>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.change-password-page {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 80vh;
|
|
padding: 1rem;
|
|
|
|
&__card {
|
|
width: 100%;
|
|
max-width: 480px;
|
|
}
|
|
|
|
&__form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
}
|
|
</style>
|