AdminModal.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <div class="admin--modal-overlay" @click.self="close">
  3. <div class="admin--modal">
  4. <div class="admin--modal-header">
  5. <h4>{{ isEdit ? '관리자 수정' : '관리자 추가' }}</h4>
  6. <button @click="close" class="admin--modal-close">&times;</button>
  7. </div>
  8. <div class="admin--modal-body">
  9. <form @submit.prevent="save">
  10. <div class="admin--form-group">
  11. <label class="admin--label">아이디 *</label>
  12. <input
  13. v-model="formData.username"
  14. type="text"
  15. class="admin--input"
  16. required
  17. :disabled="isEdit"
  18. placeholder="영문, 숫자 조합"
  19. />
  20. </div>
  21. <div class="admin--form-group">
  22. <label class="admin--label">이름 *</label>
  23. <input
  24. v-model="formData.name"
  25. type="text"
  26. class="admin--input"
  27. required
  28. placeholder="관리자 이름"
  29. />
  30. </div>
  31. <div class="admin--form-group">
  32. <label class="admin--label">이메일 *</label>
  33. <input
  34. v-model="formData.email"
  35. type="email"
  36. class="admin--input"
  37. required
  38. placeholder="example@email.com"
  39. />
  40. </div>
  41. <div class="admin--form-group" v-if="!isEdit">
  42. <label class="admin--label">비밀번호 *</label>
  43. <input
  44. v-model="formData.password"
  45. type="password"
  46. class="admin--input"
  47. :required="!isEdit"
  48. placeholder="최소 8자 이상"
  49. minlength="8"
  50. />
  51. </div>
  52. <div class="admin--form-group" v-if="!isEdit">
  53. <label class="admin--label">비밀번호 확인 *</label>
  54. <input
  55. v-model="formData.password_confirm"
  56. type="password"
  57. class="admin--input"
  58. :required="!isEdit"
  59. placeholder="비밀번호 재입력"
  60. minlength="8"
  61. />
  62. </div>
  63. <div class="admin--form-group">
  64. <label class="admin--label">역할 *</label>
  65. <select v-model="formData.role" class="admin--select" required>
  66. <option value="admin">일반 관리자</option>
  67. <option value="super_admin">슈퍼 관리자</option>
  68. </select>
  69. </div>
  70. <div class="admin--form-group">
  71. <label class="admin--label">상태 *</label>
  72. <select v-model="formData.status" class="admin--select" required>
  73. <option value="active">활성</option>
  74. <option value="inactive">비활성</option>
  75. </select>
  76. </div>
  77. <div class="admin--modal-footer">
  78. <button type="button" @click="close" class="admin--btn-secondary">
  79. 취소
  80. </button>
  81. <button type="submit" class="admin--btn-primary" :disabled="saving">
  82. {{ saving ? '저장 중...' : '저장' }}
  83. </button>
  84. </div>
  85. </form>
  86. </div>
  87. </div>
  88. </div>
  89. </template>
  90. <script setup>
  91. import { ref, computed, watch } from 'vue'
  92. const props = defineProps({
  93. admin: {
  94. type: Object,
  95. default: null
  96. }
  97. })
  98. const emit = defineEmits(['close', 'saved'])
  99. const { post, put } = useApi()
  100. const isEdit = computed(() => !!props.admin)
  101. const saving = ref(false)
  102. const formData = ref({
  103. username: '',
  104. name: '',
  105. email: '',
  106. password: '',
  107. password_confirm: '',
  108. role: 'admin',
  109. status: 'active'
  110. })
  111. // props.admin이 변경되면 formData 업데이트
  112. watch(() => props.admin, (newAdmin) => {
  113. if (newAdmin) {
  114. formData.value = {
  115. username: newAdmin.username || '',
  116. name: newAdmin.name || '',
  117. email: newAdmin.email || '',
  118. password: '',
  119. password_confirm: '',
  120. role: newAdmin.role || 'admin',
  121. status: newAdmin.status || 'active'
  122. }
  123. }
  124. }, { immediate: true })
  125. const close = () => {
  126. emit('close')
  127. }
  128. const save = async () => {
  129. // 비밀번호 확인 (신규 생성 시)
  130. if (!isEdit.value) {
  131. if (formData.value.password !== formData.value.password_confirm) {
  132. emit('saved', '비밀번호가 일치하지 않습니다.')
  133. return
  134. }
  135. if (formData.value.password.length < 8) {
  136. emit('saved', '비밀번호는 최소 8자 이상이어야 합니다.')
  137. return
  138. }
  139. }
  140. saving.value = true
  141. try {
  142. let result
  143. if (isEdit.value) {
  144. // 수정
  145. const updateData = {
  146. name: formData.value.name,
  147. email: formData.value.email,
  148. role: formData.value.role,
  149. status: formData.value.status
  150. }
  151. result = await put(`/admin/${props.admin.id}`, updateData)
  152. } else {
  153. // 생성
  154. const createData = {
  155. username: formData.value.username,
  156. name: formData.value.name,
  157. email: formData.value.email,
  158. password: formData.value.password,
  159. role: formData.value.role,
  160. status: formData.value.status
  161. }
  162. result = await post('/admin', createData)
  163. }
  164. const { data, error } = result
  165. if (error) {
  166. console.error('[AdminModal] 저장 실패:', error)
  167. const errorMessage = error.response?.data?.message || '저장에 실패했습니다.'
  168. emit('saved', errorMessage)
  169. return
  170. }
  171. if (data?.success) {
  172. emit('saved', data.message || '저장되었습니다.')
  173. }
  174. } finally {
  175. saving.value = false
  176. }
  177. }
  178. </script>