create.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <div class="admin--branch-form">
  3. <form @submit.prevent="handleSubmit" class="admin--form">
  4. <!-- 지점명 -->
  5. <div class="admin--form-group">
  6. <label class="admin--form-label">지점명 <span class="admin--required">*</span></label>
  7. <input
  8. v-model="formData.name"
  9. type="text"
  10. class="admin--form-input"
  11. placeholder="지점명을 입력하세요"
  12. required
  13. >
  14. </div>
  15. <!-- 대표번호 -->
  16. <div class="admin--form-group">
  17. <label class="admin--form-label">대표번호 <span class="admin--required">*</span></label>
  18. <input
  19. v-model="formData.phone"
  20. type="tel"
  21. class="admin--form-input"
  22. placeholder="02-1234-5678"
  23. required
  24. >
  25. </div>
  26. <!-- 주소 -->
  27. <div class="admin--form-group">
  28. <label class="admin--form-label">주소 <span class="admin--required">*</span></label>
  29. <input
  30. v-model="formData.address"
  31. type="text"
  32. class="admin--form-input"
  33. placeholder="주소를 입력하세요"
  34. required
  35. >
  36. </div>
  37. <!-- 상세주소 -->
  38. <div class="admin--form-group">
  39. <label class="admin--form-label">상세주소</label>
  40. <input
  41. v-model="formData.detail_address"
  42. type="text"
  43. class="admin--form-input"
  44. placeholder="상세주소를 입력하세요"
  45. >
  46. </div>
  47. <!-- 위도/경도 -->
  48. <div class="admin--form-group">
  49. <label class="admin--form-label">위치 좌표</label>
  50. <div class="admin--coordinate-group">
  51. <div class="admin--coordinate-item">
  52. <label>위도</label>
  53. <input
  54. v-model.number="formData.latitude"
  55. type="number"
  56. step="any"
  57. class="admin--form-input"
  58. placeholder="37.5665"
  59. >
  60. </div>
  61. <div class="admin--coordinate-item">
  62. <label>경도</label>
  63. <input
  64. v-model.number="formData.longitude"
  65. type="number"
  66. step="any"
  67. class="admin--form-input"
  68. placeholder="126.9780"
  69. >
  70. </div>
  71. </div>
  72. </div>
  73. <!-- 영업시간 -->
  74. <div class="admin--form-group">
  75. <label class="admin--form-label">영업시간</label>
  76. <textarea
  77. v-model="formData.business_hours"
  78. class="admin--form-textarea"
  79. rows="3"
  80. placeholder="평일: 09:00 - 18:00&#10;주말: 10:00 - 17:00"
  81. ></textarea>
  82. </div>
  83. <!-- 버튼 영역 -->
  84. <div class="admin--form-actions">
  85. <button
  86. type="submit"
  87. class="admin--btn admin--btn-primary"
  88. :disabled="isSaving"
  89. >
  90. {{ isSaving ? '저장 중...' : '확인' }}
  91. </button>
  92. <button
  93. type="button"
  94. class="admin--btn admin--btn-secondary"
  95. @click="goToList"
  96. >
  97. 목록
  98. </button>
  99. </div>
  100. <!-- 성공/에러 메시지 -->
  101. <div v-if="successMessage" class="admin--alert admin--alert-success">
  102. {{ successMessage }}
  103. </div>
  104. <div v-if="errorMessage" class="admin--alert admin--alert-error">
  105. {{ errorMessage }}
  106. </div>
  107. </form>
  108. </div>
  109. </template>
  110. <script setup>
  111. import { ref } from 'vue'
  112. import { useRouter } from 'vue-router'
  113. definePageMeta({
  114. layout: 'admin',
  115. middleware: ['auth']
  116. })
  117. const router = useRouter()
  118. const { post } = useApi()
  119. const isSaving = ref(false)
  120. const successMessage = ref('')
  121. const errorMessage = ref('')
  122. const formData = ref({
  123. name: '',
  124. phone: '',
  125. address: '',
  126. detail_address: '',
  127. latitude: null,
  128. longitude: null,
  129. business_hours: ''
  130. })
  131. // 폼 제출
  132. const handleSubmit = async () => {
  133. successMessage.value = ''
  134. errorMessage.value = ''
  135. // 유효성 검사
  136. if (!formData.value.name) {
  137. errorMessage.value = '지점명을 입력하세요.'
  138. return
  139. }
  140. if (!formData.value.phone) {
  141. errorMessage.value = '대표번호를 입력하세요.'
  142. return
  143. }
  144. if (!formData.value.address) {
  145. errorMessage.value = '주소를 입력하세요.'
  146. return
  147. }
  148. isSaving.value = true
  149. try {
  150. const { data, error } = await post('/branch', formData.value)
  151. if (error || !data?.success) {
  152. errorMessage.value = error?.message || data?.message || '등록에 실패했습니다.'
  153. } else {
  154. successMessage.value = data.message || '지점이 등록되었습니다.'
  155. setTimeout(() => {
  156. router.push('/admin/branch/list')
  157. }, 1000)
  158. }
  159. } catch (error) {
  160. errorMessage.value = '서버 오류가 발생했습니다.'
  161. console.error('Save error:', error)
  162. } finally {
  163. isSaving.value = false
  164. }
  165. }
  166. // 목록으로 이동
  167. const goToList = () => {
  168. router.push('/admin/branch/list')
  169. }
  170. </script>
  171. <style scoped>
  172. .admin--coordinate-group {
  173. display: flex;
  174. gap: 16px;
  175. }
  176. .admin--coordinate-item {
  177. flex: 1;
  178. }
  179. .admin--coordinate-item label {
  180. display: block;
  181. margin-bottom: 8px;
  182. font-size: 14px;
  183. color: #666;
  184. }
  185. </style>