site-info.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <div class="admin--site-info">
  3. <div v-if="isLoading" class="admin--loading">
  4. 데이터를 불러오는 중...
  5. </div>
  6. <form v-else @submit.prevent="handleSubmit" class="admin--form">
  7. <!-- 사이트명 -->
  8. <div class="admin--form-group">
  9. <label class="admin--form-label">사이트명 <span class="admin--required">*</span></label>
  10. <input
  11. v-model="formData.site_name"
  12. type="text"
  13. class="admin--form-input"
  14. placeholder="사이트명을 입력하세요"
  15. required
  16. >
  17. </div>
  18. <!-- 사이트 URL -->
  19. <div class="admin--form-group">
  20. <label class="admin--form-label">사이트 URL <span class="admin--required">*</span></label>
  21. <input
  22. v-model="formData.site_url"
  23. type="url"
  24. class="admin--form-input"
  25. placeholder="https://example.com"
  26. required
  27. >
  28. </div>
  29. <!-- 대표명 -->
  30. <div class="admin--form-group">
  31. <label class="admin--form-label">대표명 <span class="admin--required">*</span></label>
  32. <input
  33. v-model="formData.ceo_name"
  34. type="text"
  35. class="admin--form-input"
  36. placeholder="대표명을 입력하세요"
  37. required
  38. >
  39. </div>
  40. <!-- 대표 이메일 -->
  41. <div class="admin--form-group">
  42. <label class="admin--form-label">대표 이메일 <span class="admin--required">*</span></label>
  43. <input
  44. v-model="formData.ceo_email"
  45. type="email"
  46. class="admin--form-input"
  47. placeholder="email@example.com"
  48. required
  49. >
  50. </div>
  51. <!-- 전화번호 -->
  52. <div class="admin--form-group">
  53. <label class="admin--form-label">전화번호 <span class="admin--required">*</span></label>
  54. <input
  55. v-model="formData.phone"
  56. type="tel"
  57. class="admin--form-input"
  58. placeholder="02-1234-5678"
  59. required
  60. >
  61. </div>
  62. <!-- FAX번호 -->
  63. <div class="admin--form-group">
  64. <label class="admin--form-label">FAX번호</label>
  65. <input
  66. v-model="formData.fax"
  67. type="tel"
  68. class="admin--form-input"
  69. placeholder="02-1234-5679"
  70. >
  71. </div>
  72. <!-- 회사주소 -->
  73. <div class="admin--form-group">
  74. <label class="admin--form-label">회사주소 <span class="admin--required">*</span></label>
  75. <input
  76. v-model="formData.address"
  77. type="text"
  78. class="admin--form-input"
  79. placeholder="회사 주소를 입력하세요"
  80. required
  81. >
  82. </div>
  83. <!-- SMS발신번호 (다중) -->
  84. <div class="admin--form-group">
  85. <label class="admin--form-label">SMS발신번호</label>
  86. <div class="admin--multi-input-wrapper">
  87. <div
  88. v-for="(item, index) in formData.sms_sender_numbers"
  89. :key="index"
  90. class="admin--multi-input-item"
  91. >
  92. <div class="admin--sender-row">
  93. <input
  94. v-model="formData.sms_sender_numbers[index].name"
  95. type="text"
  96. class="admin--form-input"
  97. placeholder="지점명"
  98. style="flex: 1; margin-right: 10px;"
  99. >
  100. <input
  101. v-model="formData.sms_sender_numbers[index].number"
  102. type="tel"
  103. class="admin--form-input"
  104. placeholder="010-1234-5678"
  105. style="flex: 1;"
  106. >
  107. </div>
  108. <button
  109. v-if="formData.sms_sender_numbers.length > 1"
  110. type="button"
  111. class="admin--btn-remove"
  112. @click="removeSenderNumber(index)"
  113. >
  114. 삭제
  115. </button>
  116. </div>
  117. <button
  118. type="button"
  119. class="admin--btn-add"
  120. @click="addSenderNumber"
  121. >
  122. + 발신번호 추가
  123. </button>
  124. </div>
  125. </div>
  126. <!-- SMS수신번호 -->
  127. <div class="admin--form-group">
  128. <label class="admin--form-label">SMS수신번호</label>
  129. <input
  130. v-model="formData.sms_receiver_number"
  131. type="tel"
  132. class="admin--form-input"
  133. placeholder="010-9876-5432"
  134. >
  135. </div>
  136. <!-- 버튼 영역 -->
  137. <div class="admin--form-actions">
  138. <button
  139. type="submit"
  140. class="admin--btn admin--btn-primary"
  141. :disabled="isSaving"
  142. >
  143. {{ isSaving ? '저장 중...' : '저장' }}
  144. </button>
  145. </div>
  146. <!-- 성공/에러 메시지 -->
  147. <div v-if="successMessage" class="admin--alert admin--alert-success">
  148. {{ successMessage }}
  149. </div>
  150. <div v-if="errorMessage" class="admin--alert admin--alert-error">
  151. {{ errorMessage }}
  152. </div>
  153. </form>
  154. </div>
  155. </template>
  156. <script setup>
  157. import { ref, onMounted } from 'vue'
  158. definePageMeta({
  159. layout: 'admin',
  160. middleware: ['auth']
  161. })
  162. const { get, post } = useApi()
  163. const isLoading = ref(true)
  164. const isSaving = ref(false)
  165. const successMessage = ref('')
  166. const errorMessage = ref('')
  167. const siteInfoId = ref(null)
  168. const formData = ref({
  169. site_name: '',
  170. site_url: '',
  171. ceo_name: '',
  172. ceo_email: '',
  173. phone: '',
  174. fax: '',
  175. address: '',
  176. sms_sender_numbers: [{ name: '', number: '' }],
  177. sms_receiver_number: ''
  178. })
  179. // 데이터 로드
  180. const loadSiteInfo = async () => {
  181. isLoading.value = true
  182. const { data, error } = await get('/basic/site-info')
  183. console.log('[SiteInfo] API 응답:', { data, error })
  184. // API 응답: { success: true, data: {...}, message }
  185. if (data?.success && data?.data) {
  186. const siteData = data.data
  187. siteInfoId.value = siteData.id
  188. // SMS 발신번호 데이터 변환 (기존 문자열 배열 → 객체 배열)
  189. let senderNumbers = [{ name: '', number: '' }]
  190. if (siteData.sms_sender_numbers && siteData.sms_sender_numbers.length > 0) {
  191. senderNumbers = siteData.sms_sender_numbers.map(item => {
  192. // 이미 객체 형태인 경우
  193. if (typeof item === 'object' && item.name !== undefined) {
  194. return item
  195. }
  196. // 문자열인 경우 (기존 데이터)
  197. return { name: '', number: item }
  198. })
  199. }
  200. formData.value = {
  201. site_name: siteData.site_name || '',
  202. site_url: siteData.site_url || '',
  203. ceo_name: siteData.ceo_name || '',
  204. ceo_email: siteData.ceo_email || '',
  205. phone: siteData.phone || '',
  206. fax: siteData.fax || '',
  207. address: siteData.address || '',
  208. sms_sender_numbers: senderNumbers,
  209. sms_receiver_number: siteData.sms_receiver_number || ''
  210. }
  211. }
  212. isLoading.value = false
  213. }
  214. // 발신번호 추가
  215. const addSenderNumber = () => {
  216. formData.value.sms_sender_numbers.push({ name: '', number: '' })
  217. }
  218. // 발신번호 삭제
  219. const removeSenderNumber = (index) => {
  220. formData.value.sms_sender_numbers.splice(index, 1)
  221. }
  222. // 폼 제출
  223. const handleSubmit = async () => {
  224. successMessage.value = ''
  225. errorMessage.value = ''
  226. isSaving.value = true
  227. try {
  228. // 빈 발신번호 제거 (지점명 또는 번호가 비어있지 않은 것만)
  229. const cleanedSenderNumbers = formData.value.sms_sender_numbers.filter(
  230. sender => (sender.name && sender.name.trim() !== '') || (sender.number && sender.number.trim() !== '')
  231. )
  232. const submitData = {
  233. ...formData.value,
  234. sms_sender_numbers: cleanedSenderNumbers
  235. }
  236. // POST로 통일 (등록/수정 모두)
  237. const { data, error } = await post('/basic/site-info', submitData)
  238. console.log('[SiteInfo] 저장 응답:', { data, error })
  239. if (error) {
  240. errorMessage.value = error.message || '저장에 실패했습니다.'
  241. } else if (data?.success) {
  242. successMessage.value = data.message || '사이트 정보가 저장되었습니다.'
  243. // 저장된 데이터로 업데이트
  244. if (data.data) {
  245. const siteData = data.data
  246. siteInfoId.value = siteData.id
  247. // SMS 발신번호 데이터 변환 (기존 문자열 배열 → 객체 배열)
  248. let senderNumbers = [{ name: '', number: '' }]
  249. if (siteData.sms_sender_numbers && siteData.sms_sender_numbers.length > 0) {
  250. senderNumbers = siteData.sms_sender_numbers.map(item => {
  251. // 이미 객체 형태인 경우
  252. if (typeof item === 'object' && item.name !== undefined) {
  253. return item
  254. }
  255. // 문자열인 경우 (기존 데이터)
  256. return { name: '', number: item }
  257. })
  258. }
  259. formData.value = {
  260. site_name: siteData.site_name || '',
  261. site_url: siteData.site_url || '',
  262. ceo_name: siteData.ceo_name || '',
  263. ceo_email: siteData.ceo_email || '',
  264. phone: siteData.phone || '',
  265. fax: siteData.fax || '',
  266. address: siteData.address || '',
  267. sms_sender_numbers: senderNumbers,
  268. sms_receiver_number: siteData.sms_receiver_number || ''
  269. }
  270. }
  271. // 3초 후 메시지 자동 제거
  272. setTimeout(() => {
  273. successMessage.value = ''
  274. }, 3000)
  275. } else {
  276. errorMessage.value = '저장에 실패했습니다.'
  277. }
  278. } catch (error) {
  279. errorMessage.value = '서버 오류가 발생했습니다.'
  280. console.error('Save error:', error)
  281. } finally {
  282. isSaving.value = false
  283. }
  284. }
  285. onMounted(() => {
  286. loadSiteInfo()
  287. })
  288. </script>