join.vue 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. <template>
  2. <div class="login-wrap type--join">
  3. <!-- header -->
  4. <div class="login--header">
  5. <div class="login--header--l">
  6. <div class="logo">
  7. <!-- prettier-ignore -->
  8. SHOPDELI
  9. </div>
  10. </div>
  11. <div class="login--header--r"></div>
  12. </div>
  13. <!-- login -->
  14. <div class="login-box type--join">
  15. <div class="login-r">
  16. <!-- <h2 class="mk--title"></h2> -->
  17. <div class="tit-login">
  18. <strong>{{ titleh }} 회원가입</strong>
  19. <!-- <span><i>*</i>필수입력 항목</span> -->
  20. </div>
  21. <div v-show="form.formValue0 === 'Y'" class="login-input-wrap">
  22. <div class="txt-field-box">
  23. <v-text-field
  24. :disabled="useStore.getSnsTempData?.ID ? true : false"
  25. v-model="form.formValue1"
  26. placeholder="아이디를 입력해주세요"
  27. class="custom-input"
  28. @blur="checkId"
  29. ></v-text-field>
  30. <v-btn v-if="!useStore.getSnsTempData?.ID" class="ml-2 custom-btn mini btn-purple" @click="checkId"
  31. >중복확인</v-btn
  32. >
  33. </div>
  34. <div class="txt-field-box">
  35. <v-text-field
  36. v-model="form.formValue2"
  37. :type="visible ? 'text' : 'password'"
  38. placeholder="비밀번호를 입력해주세요."
  39. class="custom-input"
  40. id="password"
  41. ></v-text-field>
  42. <i
  43. class="ico-eye"
  44. @click.stop="toggleVisibility"
  45. :class="visible ? 'eye-on' : 'eye-off'"
  46. ></i>
  47. <i class="ico"></i>
  48. </div>
  49. <div class="txt-field-box">
  50. <v-text-field
  51. v-model="form.formValue3"
  52. :type="visible ? 'text' : 'password'"
  53. placeholder="비밀번호를 한 번 더 입력해주세요."
  54. class="custom-input"
  55. ></v-text-field>
  56. <i
  57. class="ico-eye"
  58. @click.stop="toggleVisibility"
  59. :class="visible ? 'eye-on' : 'eye-off'"
  60. ></i>
  61. <i class="ico"></i>
  62. </div>
  63. <div class="txt-field-box">
  64. <v-text-field
  65. v-model="form.formValue4"
  66. :maxlength="20"
  67. :counter="20"
  68. :placeholder="'닉네임을 입력해주세요.'"
  69. class="custom-input"
  70. ></v-text-field>
  71. </div>
  72. <div class="txt-field-box">
  73. <v-text-field
  74. v-model="form.formValue5"
  75. :disabled="useStore.getSnsTempData?.NAME ? true : false"
  76. :maxlength="20"
  77. :counter="20"
  78. placeholder="이름을 입력해주세요"
  79. class="custom-input"
  80. ></v-text-field>
  81. </div>
  82. <div class="txt-field-box">
  83. <v-select
  84. v-model="form.formValue6"
  85. :items="form.formValueItems6"
  86. item-title="text"
  87. item-value="value"
  88. class="custom-select"
  89. ></v-select>
  90. </div>
  91. <div class="txt-field-box">
  92. <v-text-field
  93. v-model="form.formValue7"
  94. placeholder="소셜 ID 또는 주소를 입력해주세요."
  95. class="custom-input"
  96. ></v-text-field>
  97. </div>
  98. <div class="txt-field-box phone">
  99. <v-text-field
  100. placeholder=""
  101. class="custom-input"
  102. v-model="form.formValue8"
  103. :maxlength="4"
  104. ></v-text-field>
  105. -
  106. <v-text-field
  107. placeholder="1234"
  108. class="custom-input"
  109. v-model="form.formValue9"
  110. :maxlength="4"
  111. ></v-text-field>
  112. -
  113. <v-text-field
  114. placeholder="5678"
  115. class="custom-input"
  116. v-model="form.formValue10"
  117. :maxlength="4"
  118. ></v-text-field>
  119. </div>
  120. <div class="txt-field-box email">
  121. <v-text-field
  122. v-model="form.formValue12"
  123. :disabled="useStore.getSnsTempData?.EMAIL"
  124. class="custom-input"
  125. placeholder=""
  126. ></v-text-field>
  127. <span>@</span>
  128. <v-combobox
  129. :disabled="useStore.getSnsTempData?.EMAIL ? true : false"
  130. v-model="form.formValue11"
  131. :items="['gmail.com', 'naver.com', 'daum.net', 'hotmail.com', 'yahoo.com']"
  132. placeholder="도메인 선택 또는 직접 입력"
  133. class="custom-select"
  134. ></v-combobox>
  135. </div>
  136. <div class="d-flex agree--box">
  137. <v-checkbox hide-details class="custom-check type2" v-model="formVendor.formValue13" @click="handlePrivacyModal">
  138. <template v-slot:label>개인정보약관동의(필수)</template>
  139. </v-checkbox>
  140. <v-checkbox hide-details class="custom-check type2" v-model="formVendor.formValue14" @click="handleThirdPartyModal">
  141. <template v-slot:label>제3자 정보동의(선택)</template>
  142. </v-checkbox>
  143. </div>
  144. <!-- <div class="txt-field-box">
  145. <v-textarea
  146. v-model="form.formValue13"
  147. placeholder="자기소개를 입력해주세요. 벤더사들이 참고할 수 있도록 작성해주세요."
  148. class="custom-textarea"
  149. rows="3"
  150. ></v-textarea>
  151. </div> -->
  152. </div>
  153. <div v-show="form.formValue0 === 'N'" class="login-input-wrap">
  154. <div class="txt-field-box">
  155. <v-text-field
  156. v-model="formVendor.formValue1"
  157. placeholder="아이디를 입력해주세요"
  158. class="custom-input"
  159. ></v-text-field>
  160. <v-btn
  161. v-if="!useStore.getSnsTempData?.ID"
  162. class="ml-2 custom-btn mini btn-purple"
  163. @click="checkIdVendor"
  164. >중복확인</v-btn
  165. >
  166. </div>
  167. <div class="txt-field-box">
  168. <v-text-field
  169. v-model="formVendor.formValue2"
  170. :type="visible ? 'text' : 'password'"
  171. placeholder="패스워드를 입력해주세요."
  172. class="custom-input"
  173. id="password"
  174. ></v-text-field>
  175. <i
  176. class="ico-eye"
  177. @click.stop="toggleVisibility"
  178. :class="visible ? 'eye-on' : 'eye-off'"
  179. ></i>
  180. <i class="ico"></i>
  181. </div>
  182. <div class="txt-field-box">
  183. <v-text-field
  184. v-model="formVendor.formValue3"
  185. :type="visible ? 'text' : 'password'"
  186. placeholder="패스워드 확인"
  187. class="custom-input"
  188. ></v-text-field>
  189. <i
  190. class="ico-eye"
  191. @click.stop="toggleVisibility"
  192. :class="visible ? 'eye-on' : 'eye-off'"
  193. ></i>
  194. <i class="ico"></i>
  195. </div>
  196. <div class="txt-field-box">
  197. <v-text-field
  198. v-model="formVendor.formValue4"
  199. :maxlength="20"
  200. :counter="20"
  201. placeholder="회사명"
  202. class="custom-input"
  203. ></v-text-field>
  204. </div>
  205. <div class="txt-field-box">
  206. <v-text-field
  207. v-model="formVendor.formValue5"
  208. :maxlength="20"
  209. :counter="20"
  210. placeholder="담당자 명"
  211. class="custom-input"
  212. ></v-text-field>
  213. </div>
  214. <div class="txt-field-box phone">
  215. <v-text-field
  216. placeholder=""
  217. class="custom-input"
  218. v-model="formVendor.formValue8"
  219. :maxlength="4"
  220. ></v-text-field>
  221. -
  222. <v-text-field
  223. placeholder="1234"
  224. class="custom-input"
  225. v-model="formVendor.formValue9"
  226. :maxlength="4"
  227. ></v-text-field>
  228. -
  229. <v-text-field
  230. placeholder="5678"
  231. class="custom-input"
  232. v-model="formVendor.formValue10"
  233. :maxlength="4"
  234. ></v-text-field>
  235. </div>
  236. <div class="txt-field-box email">
  237. <v-text-field
  238. v-model="formVendor.formValue12"
  239. class="custom-input"
  240. placeholder=""
  241. ></v-text-field>
  242. <span>@</span>
  243. <v-combobox
  244. v-model="formVendor.formValue11"
  245. :items="['gmail.com', 'naver.com', 'daum.net', 'hotmail.com', 'yahoo.com']"
  246. placeholder="도메인 선택 또는 직접 입력"
  247. class="custom-select"
  248. ></v-combobox>
  249. </div>
  250. <div class="mt-5 d-flex agree--box">
  251. <v-checkbox class="custom-check type2" v-model="formVendor.formValue13" @click="handlePrivacyModal">
  252. <template v-slot:label>개인정보약관동의</template>
  253. </v-checkbox>
  254. <v-checkbox class="custom-check type2" v-model="formVendor.formValue14" @click="handleThirdPartyModal">
  255. <template v-slot:label>제3자 정보동의</template>
  256. </v-checkbox>
  257. </div>
  258. </div>
  259. <div class="login-btn-wrap">
  260. <v-btn
  261. v-show="form.formValue0 === 'Y'"
  262. class="custom-btn btn-blue"
  263. @click.stop="joinMember('influence')"
  264. >회원가입</v-btn
  265. >
  266. <v-btn
  267. v-show="typeParam === 'vendor'"
  268. class="custom-btn btn-blue"
  269. @click.stop="joinMember('vendor')"
  270. >회원가입</v-btn
  271. >
  272. <v-btn
  273. v-show="typeParam === 'brand'"
  274. class="custom-btn btn-blue"
  275. @click.stop="joinMember('brand')"
  276. >회원가입</v-btn
  277. >
  278. </div>
  279. </div>
  280. </div>
  281. <!-- footer -->
  282. <div class="login-footer">
  283. <div class="login--footer--l">
  284. <p>COPYRIGHT@2025 SHOPDELI INC. ALL RIGHTS RESERVED.</p>
  285. <p>마포구 합정동</p>
  286. </div>
  287. </div>
  288. <!-- 개인정보 약관 모달 -->
  289. <div v-if="showPrivacyModal" class="modal-overlay" @click="closePrivacyModal">
  290. <div class="modal-content" @click.stop>
  291. <div class="modal-header">
  292. <h3>개인정보 수집 및 이용 동의</h3>
  293. <button class="modal-close-btn" @click="closePrivacyModal">×</button>
  294. </div>
  295. <div class="modal-body">
  296. <div class="privacy-content">
  297. <h4>1. 개인정보 수집 목적</h4>
  298. <p>서비스 제공, 회원 관리, 고객 상담, 마케팅 활용</p>
  299. <h4>2. 수집하는 개인정보 항목</h4>
  300. <p>필수항목: 이름, 이메일, 전화번호, 회원구분</p>
  301. <p>선택항목: 닉네임, SNS 정보</p>
  302. <h4>3. 개인정보 보유 및 이용기간</h4>
  303. <p>회원 탈퇴 시까지 또는 법정 보존기간</p>
  304. <h4>4. 개인정보 제3자 제공</h4>
  305. <p>원칙적으로 개인정보를 제3자에게 제공하지 않습니다. 단, 법령에 의한 경우는 예외로 합니다.</p>
  306. <h4>5. 개인정보 처리 위탁</h4>
  307. <p>서비스 운영을 위해 필요한 경우 개인정보 처리를 위탁할 수 있습니다.</p>
  308. <h4>6. 개인정보 보호책임자</h4>
  309. <p>개인정보 보호책임자: 홍길동 (privacy@company.com)</p>
  310. <p class="privacy-notice">
  311. 위의 개인정보 수집 및 이용에 대한 동의를 거부할 권리가 있습니다.
  312. 그러나 동의를 거부할 경우 서비스 이용이 제한될 수 있습니다.
  313. </p>
  314. </div>
  315. </div>
  316. <div class="modal-footer">
  317. <button class="btn-disagree" @click="disagreePrivacy">미동의</button>
  318. <button class="btn-agree" @click="agreePrivacy">동의</button>
  319. </div>
  320. </div>
  321. </div>
  322. <!-- 제3자 정보 제공 동의 모달 -->
  323. <div v-if="showThirdPartyModal" class="modal-overlay" @click="closeThirdPartyModal">
  324. <div class="modal-content" @click.stop>
  325. <div class="modal-header">
  326. <h3>제3자 정보 제공 동의</h3>
  327. <button class="modal-close-btn" @click="closeThirdPartyModal">×</button>
  328. </div>
  329. <div class="modal-body">
  330. <div class="privacy-content">
  331. <h4>1. 제공받는 자</h4>
  332. <p>협력업체, 마케팅 대행사</p>
  333. <h4>2. 제공하는 개인정보 항목</h4>
  334. <p>이름, 이메일, 전화번호, 관심사</p>
  335. <h4>3. 제공 목적</h4>
  336. <p>맞춤형 광고, 이벤트 정보 제공, 협력업체 서비스 안내</p>
  337. <h4>4. 보유 및 이용기간</h4>
  338. <p>제공 목적 달성 시까지 또는 동의 철회 시까지</p>
  339. <p class="privacy-notice">
  340. 제3자 정보 제공에 대한 동의는 선택사항이며, 동의하지 않아도 기본 서비스 이용에는 제한이 없습니다.
  341. </p>
  342. </div>
  343. </div>
  344. <div class="modal-footer">
  345. <button class="btn-disagree" @click="disagreeThirdParty">미동의</button>
  346. <button class="btn-agree" @click="agreeThirdParty">동의</button>
  347. </div>
  348. </div>
  349. </div>
  350. </div>
  351. </template>
  352. <script setup>
  353. /************************
  354. * import
  355. ************************/
  356. const { $log, $toast, $eventBus } = useNuxtApp();
  357. /************************
  358. * layout setting
  359. ************************/
  360. definePageMeta({
  361. layout: "loginlayout",
  362. });
  363. /************************
  364. * 전역
  365. ************************/
  366. const titleh = ref("인플루언서");
  367. const useStore = useAuthStore();
  368. const randomString = ref("");
  369. // 모달 상태
  370. const showPrivacyModal = ref(false);
  371. const showThirdPartyModal = ref(false);
  372. // 중복확인 상태
  373. const isIdChecked = ref(false);
  374. const isIdCheckPassedInfluencer = ref(false);
  375. const isIdCheckPassedVendor = ref(false);
  376. // 비밀번호 표시/숨김 상태
  377. const visible = ref(false);
  378. //인플루언서 폼
  379. const form = ref({
  380. formValue0: "Y",
  381. formValue1: useStore.getSnsTempData?.ID || "", // 아이디
  382. formValue2: "", // 패스워드
  383. formValue3: "", // 패스워드 확인
  384. formValue4: "", // 닉네임
  385. formValue5: useStore.getSnsTempData?.NAME || "", // 이름
  386. formValue6: null, // 소셜 채널
  387. formValueItems6: [
  388. {
  389. text: "소셜 채널 선택",
  390. value: null,
  391. disabled: true,
  392. },
  393. {
  394. text: "유튜브",
  395. value: "youtube",
  396. },
  397. {
  398. text: "인스타그램",
  399. value: "instagram",
  400. },
  401. {
  402. text: "네이버 블로그",
  403. value: "naverblog",
  404. },
  405. ], // 소셜 채널 아이템
  406. formValue7: "", // 소셜 ID 또는 주소
  407. formValue8: "010", // 휴대폰1
  408. formValue9: "", // 휴대폰2
  409. formValue10: "", // 휴대폰3
  410. formValue11: null, // 이메일 도메인 (combobox)
  411. formValue12: "", // 이메일2
  412. formValue13: "", // 자기소개
  413. });
  414. //밴더 폼
  415. const formVendor = ref({
  416. formValue0: "",
  417. formValue1: "", // 아이디
  418. formValue2: "", // 패스워드
  419. formValue3: "", // 패스워드 확인
  420. formValue4: "", // 닉네임
  421. formValue5: "", // 이름
  422. formValue6: "", // 소셜 채널
  423. formValue7: "", // 소셜 ID 또는 주소
  424. formValue8: "010", // 휴대폰1
  425. formValue9: "", // 휴대폰2
  426. formValue10: "", // 휴대폰3
  427. formValue11: null, // 이메일 도메인 (combobox)
  428. formValue12: "",
  429. formValue13: "", // 개인정보약관동의
  430. formValue14: "", // 제3자 정보동의
  431. });
  432. /************************
  433. * 함수
  434. ************************/
  435. const generateRandomAlphanumeric = (length) => {
  436. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  437. let result = "";
  438. const charsLength = chars.length;
  439. for (let i = 0; i < length; i++) {
  440. result += chars.charAt(Math.floor(Math.random() * charsLength));
  441. }
  442. return result;
  443. };
  444. // 비밀번호 표시/숨김 토글
  445. const toggleVisibility = () => {
  446. visible.value = !visible.value;
  447. };
  448. const checkId = async () => {
  449. if (!form.value.formValue1) {
  450. $toast.error("아이디를 입력해주세요.");
  451. return;
  452. }
  453. // 아이디 형식 검사 (영문, 숫자 조합 6~20자)
  454. const idRegex = /^[a-zA-Z0-9]{6,20}$/;
  455. if (!idRegex.test(form.value.formValue1)) {
  456. $toast.error("아이디는 영문, 숫자 조합 6~20자로 입력해주세요.");
  457. return;
  458. }
  459. try {
  460. const response = await useAxios().post("/auth/checkId", {
  461. id: form.value.formValue1,
  462. type: "influence",
  463. });
  464. if (response.data.isDuplicate) {
  465. $toast.error("이미 사용중인 아이디입니다.");
  466. isIdCheckPassedInfluencer.value = false;
  467. } else {
  468. $toast.success("사용 가능한 아이디입니다.");
  469. isIdCheckPassedInfluencer.value = true;
  470. }
  471. } catch (error) {
  472. console.error("ID check error:", error);
  473. $toast.error("아이디 중복 확인 중 오류가 발생했습니다.");
  474. isIdCheckPassedInfluencer.value = false;
  475. }
  476. };
  477. const checkIdVendor = async () => {
  478. if (!formVendor.value.formValue1) {
  479. $toast.error("아이디를 입력해주세요.");
  480. return;
  481. }
  482. // 아이디 형식 검사 (영문, 숫자 조합 6~20자)
  483. const idRegex = /^[a-zA-Z0-9]{6,20}$/;
  484. if (!idRegex.test(formVendor.value.formValue1)) {
  485. $toast.error("아이디는 영문, 숫자 조합 6~20자로 입력해주세요.");
  486. return;
  487. }
  488. try {
  489. const response = await useAxios().post("/auth/checkId", {
  490. id: formVendor.value.formValue1,
  491. type: typeParam.value,
  492. });
  493. if (response.data.isDuplicate) {
  494. $toast.error("이미 사용중인 아이디입니다.");
  495. isIdCheckPassedVendor.value = false;
  496. } else {
  497. $toast.success("사용 가능한 아이디입니다.");
  498. isIdCheckPassedVendor.value = true;
  499. }
  500. } catch (error) {
  501. console.error("ID check error:", error);
  502. $toast.error("아이디 중복 확인 중 오류가 발생했습니다.");
  503. isIdCheckPassedVendor.value = false;
  504. }
  505. };
  506. // 회원가입 전에 유효성 검사
  507. const joinMember = async (id_type) => {
  508. // 필수값 검사
  509. if (id_type === "influence") {
  510. // 인플루언서 필수값 검사
  511. if (!form.value.formValue1) {
  512. $toast.error("아이디를 입력해주세요.");
  513. return;
  514. }
  515. if (!form.value.formValue2) {
  516. $toast.error("비밀번호를 입력해주세요.");
  517. return;
  518. }
  519. if (!form.value.formValue3) {
  520. $toast.error("비밀번호 확인을 입력해주세요.");
  521. return;
  522. }
  523. if (form.value.formValue2 !== form.value.formValue3) {
  524. $toast.error("비밀번호가 일치하지 않습니다.");
  525. return;
  526. }
  527. if (!form.value.formValue4) {
  528. $toast.error("닉네임을 입력해주세요.");
  529. return;
  530. }
  531. if (!form.value.formValue5) {
  532. $toast.error("이름을 입력해주세요.");
  533. return;
  534. }
  535. if (!form.value.formValue8 || !form.value.formValue9 || !form.value.formValue10) {
  536. $toast.error("연락처를 입력해주세요.");
  537. return;
  538. }
  539. if (!form.value.formValue12) {
  540. $toast.error("이메일을 입력해주세요.");
  541. return;
  542. }
  543. // 개인정보약관동의 필수 체크
  544. if (!formVendor.value.formValue13) {
  545. $toast.error("개인정보약관동의는 필수입니다.");
  546. return;
  547. }
  548. // SNS 가입이 아닌 경우에만 아이디 중복확인 체크
  549. if (!useStore.getSnsTempData?.ID && !isIdCheckPassedInfluencer.value) {
  550. $toast.error("아이디 중복확인을 먼저 해주세요.");
  551. return;
  552. }
  553. } else if (id_type === "vendor" || id_type === "brand") {
  554. // 벤더/브랜드 필수값 검사
  555. if (!formVendor.value.formValue1) {
  556. $toast.error("아이디를 입력해주세요.");
  557. return;
  558. }
  559. if (!formVendor.value.formValue2) {
  560. $toast.error("비밀번호를 입력해주세요.");
  561. return;
  562. }
  563. if (!formVendor.value.formValue3) {
  564. $toast.error("비밀번호 확인을 입력해주세요.");
  565. return;
  566. }
  567. if (formVendor.value.formValue2 !== formVendor.value.formValue3) {
  568. $toast.error("비밀번호가 일치하지 않습니다.");
  569. return;
  570. }
  571. if (!formVendor.value.formValue4) {
  572. $toast.error("회사명을 입력해주세요.");
  573. return;
  574. }
  575. if (!formVendor.value.formValue5) {
  576. $toast.error("담당자명을 입력해주세요.");
  577. return;
  578. }
  579. if (!formVendor.value.formValue8 || !formVendor.value.formValue9 || !formVendor.value.formValue10) {
  580. $toast.error("연락처를 입력해주세요.");
  581. return;
  582. }
  583. if (!formVendor.value.formValue12) {
  584. $toast.error("이메일을 입력해주세요.");
  585. return;
  586. }
  587. // 개인정보약관동의 필수 체크
  588. if (!formVendor.value.formValue13) {
  589. $toast.error("개인정보약관동의는 필수입니다.");
  590. return;
  591. }
  592. // SNS 가입이 아닌 경우에만 아이디 중복확인 체크
  593. if (!useStore.getSnsTempData?.ID && !isIdCheckPassedVendor.value) {
  594. $toast.error("아이디 중복확인을 먼저 해주세요.");
  595. return;
  596. }
  597. }
  598. let param = {
  599. id: "회원가입",
  600. title: "회원가입",
  601. content: "회원가입을 진행하시겠습니까?",
  602. yes: {
  603. text: "확인",
  604. isProc: true,
  605. param: "",
  606. },
  607. no: {
  608. text: "취소",
  609. isProc: false,
  610. },
  611. };
  612. // 현재 회원가입 타입 저장
  613. currentJoinType.value = id_type;
  614. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  615. return;
  616. };
  617. // 실제 회원가입 처리 함수
  618. const processJoinMember = async (id_type) => {
  619. let _req = "";
  620. let _api = "";
  621. if (id_type === "influence") {
  622. _api = "/auth/joinmember";
  623. _req = {
  624. ID: form.value.formValue1,
  625. PASSWORD: form.value.formValue2,
  626. NAME: form.value.formValue5,
  627. NICK_NAME: form.value.formValue4 || "", //닉네임 없으면 빈문자
  628. PHONE: `${form.value.formValue8}-${form.value.formValue9}-${form.value.formValue10}`,
  629. EMAIL: `${form.value.formValue12}@${form.value.formValue11 || ''}`,
  630. SNS_TYPE: form.value.formValue6,
  631. SNS_LINK_ID: form.value.formValue7,
  632. ADD_INFO1: form.value.formValue13,
  633. PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
  634. THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
  635. GOOGLE_REFRESH_TOKEN: useStore.getSnsTempData?.GOOGLE_REFRESH_TOKEN || "",
  636. TYPE: useStore.getSnsTempData ? "1" : "0", // SNS 가입일경우 1, 일반회원 가입일경우 0
  637. };
  638. } else if (id_type === "vendor") {
  639. _api = "/auth/joinvendor";
  640. _req = {
  641. ID: formVendor.value.formValue1,
  642. PASSWORD: formVendor.value.formValue3,
  643. COMPANY_NAME: formVendor.value.formValue4,
  644. COMPANY_NUMBER: (randomString.value = generateRandomAlphanumeric(100)),
  645. NAME: formVendor.value.formValue5,
  646. HP: `${formVendor.value.formValue8}-${formVendor.value.formValue9}-${formVendor.value.formValue10}`,
  647. EMAIL: `${formVendor.value.formValue12}@${formVendor.value.formValue11 || ''}`,
  648. PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
  649. THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
  650. }
  651. } else {
  652. _api = "/auth/joinbrand";
  653. _req = {
  654. ID: formVendor.value.formValue1,
  655. PASSWORD: formVendor.value.formValue3,
  656. COMPANY_NAME: formVendor.value.formValue4,
  657. COMPANY_NUMBER: (randomString.value = generateRandomAlphanumeric(100)),
  658. NAME: formVendor.value.formValue5,
  659. HP: `${formVendor.value.formValue8}-${formVendor.value.formValue9}-${formVendor.value.formValue10}`,
  660. EMAIL: `${formVendor.value.formValue12}@${formVendor.value.formValue11 || ''}`,
  661. PRIVACY_AGREE: formVendor.value.formValue13 ? "Y" : "N", // 개인정보약관동의
  662. THIRDPARTY_AGREE: formVendor.value.formValue14 ? "Y" : "N", // 제3자 정보동의
  663. };
  664. }
  665. useAxios()
  666. .post(_api, _req)
  667. .then((res) => {
  668. $toast.success("회원가입이 완료되었습니다!");
  669. if (_req.TYPE === "1") {
  670. // SNS 가입일 경우
  671. useStore.setTempData("");
  672. setTimeout(() => {
  673. useUtil.setPageMove("/");
  674. }, 1500);
  675. return;
  676. }
  677. // 일반 회원가입 완료 후 메인페이지로 이동
  678. setTimeout(() => {
  679. useUtil.setPageMove("/");
  680. }, 1500);
  681. })
  682. .catch((error) => {
  683. if (error.response) {
  684. //console.log("status:", error.response.status, "data:", error.response.data);
  685. // 안전하게 errCode, message 접근
  686. const errData = error.response.data || {};
  687. const errCode = errData.errCode || errData.errorCode || errData.code || "";
  688. const errMsg = errData.message || "알 수 없는 오류가 발생했습니다.";
  689. //console.log("errCode:", errCode, "message:", errMsg);
  690. } else {
  691. //console.log("error:", error.message, error.code);
  692. }
  693. if (error.response?.status) {
  694. fnLoginSet(error.response.data.messages.message);
  695. }
  696. $log.debug("[join][fnIdPwCheck][error]");
  697. })
  698. .finally(() => {
  699. $log.debug("[join][fnIdPwCheck][finished]");
  700. });
  701. };
  702. // 현재 회원가입 타입을 저장하는 변수
  703. let currentJoinType = ref('');
  704. // 개인정보 약관 모달 함수들
  705. const handlePrivacyModal = (event) => {
  706. event.preventDefault();
  707. showPrivacyModal.value = true;
  708. // 체크 상태 초기화 (모달에서 동의해야만 체크됨)
  709. formVendor.value.formValue13 = false;
  710. };
  711. const closePrivacyModal = () => {
  712. showPrivacyModal.value = false;
  713. };
  714. const agreePrivacy = () => {
  715. formVendor.value.formValue13 = true;
  716. showPrivacyModal.value = false;
  717. $toast.success('개인정보 수집 및 이용에 동의했습니다.');
  718. };
  719. const disagreePrivacy = () => {
  720. formVendor.value.formValue13 = false;
  721. showPrivacyModal.value = false;
  722. $toast.info('개인정보 수집 및 이용에 동의하지 않았습니다.');
  723. };
  724. // 제3자 정보 제공 모달 함수들
  725. const handleThirdPartyModal = (event) => {
  726. event.preventDefault();
  727. showThirdPartyModal.value = true;
  728. // 체크 상태 초기화 (모달에서 동의해야만 체크됨)
  729. formVendor.value.formValue14 = false;
  730. };
  731. const closeThirdPartyModal = () => {
  732. showThirdPartyModal.value = false;
  733. };
  734. const agreeThirdParty = () => {
  735. formVendor.value.formValue14 = true;
  736. showThirdPartyModal.value = false;
  737. $toast.success('제3자 정보 제공에 동의했습니다.');
  738. };
  739. const disagreeThirdParty = () => {
  740. formVendor.value.formValue14 = false;
  741. showThirdPartyModal.value = false;
  742. $toast.info('제3자 정보 제공에 동의하지 않았습니다.');
  743. };
  744. /************************
  745. * 반응형 변수
  746. ************************/
  747. const route = useRoute();
  748. const typeParam = ref(route.query.type);
  749. /************************
  750. * 마운트
  751. ************************/
  752. // 아이디 변경 시 중복확인 상태 초기화
  753. watch(() => form.value.formValue1, () => {
  754. isIdCheckPassedInfluencer.value = false;
  755. });
  756. watch(() => formVendor.value.formValue1, () => {
  757. isIdCheckPassedVendor.value = false;
  758. });
  759. onMounted(() => {
  760. if(typeParam.value === "influence"){
  761. (form.value.formValue0 = "Y");
  762. titleh.value = "인플루언서"
  763. } else if(typeParam.value === "vendor"){
  764. (form.value.formValue0 = "N") ;
  765. titleh.value = "벤더사"
  766. } else {
  767. (form.value.formValue0 = "N") ;
  768. titleh.value = "브랜드사"
  769. }
  770. if (useStore.getSnsTempData?.EMAIL) {
  771. form.value.formValue12 = useStore.getSnsTempData.EMAIL;
  772. form.value.formValue11 = "direct"; // 이메일 직접입력으로 설정
  773. }
  774. // 팝업 확인 이벤트 리스너
  775. $eventBus.off('회원가입_PROC_YES');
  776. $eventBus.on('회원가입_PROC_YES', () => {
  777. processJoinMember(currentJoinType.value);
  778. });
  779. });
  780. </script>
  781. <style scoped>
  782. /* 모달 스타일 */
  783. .modal-overlay {
  784. position: fixed;
  785. top: 0;
  786. left: 0;
  787. right: 0;
  788. bottom: 0;
  789. background: rgba(0, 0, 0, 0.5);
  790. display: flex;
  791. align-items: center;
  792. justify-content: center;
  793. z-index: 1000;
  794. }
  795. .modal-content {
  796. background: white;
  797. border-radius: 12px;
  798. width: 90%;
  799. max-width: 600px;
  800. max-height: 80vh;
  801. overflow: hidden;
  802. box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
  803. }
  804. .modal-header {
  805. display: flex;
  806. justify-content: space-between;
  807. align-items: center;
  808. padding: 20px 24px;
  809. border-bottom: 1px solid #e0e0e0;
  810. background: #f8f9fa;
  811. }
  812. .modal-header h3 {
  813. margin: 0;
  814. color: #333;
  815. font-size: 1.2rem;
  816. }
  817. .modal-close-btn {
  818. background: none;
  819. border: none;
  820. font-size: 24px;
  821. cursor: pointer;
  822. color: #666;
  823. padding: 0;
  824. width: 30px;
  825. height: 30px;
  826. display: flex;
  827. align-items: center;
  828. justify-content: center;
  829. }
  830. .modal-close-btn:hover {
  831. color: #333;
  832. }
  833. .modal-body {
  834. padding: 24px;
  835. max-height: 50vh;
  836. overflow-y: auto;
  837. }
  838. .privacy-content h4 {
  839. margin: 20px 0 8px 0;
  840. color: #333;
  841. font-size: 1rem;
  842. font-weight: 600;
  843. }
  844. .privacy-content h4:first-child {
  845. margin-top: 0;
  846. }
  847. .privacy-content p {
  848. margin: 8px 0;
  849. color: #666;
  850. line-height: 1.5;
  851. }
  852. .privacy-notice {
  853. background: #f8f9fa;
  854. padding: 12px;
  855. border-radius: 6px;
  856. margin-top: 20px;
  857. font-size: 14px;
  858. color: #555;
  859. }
  860. .modal-footer {
  861. display: flex;
  862. gap: 12px;
  863. padding: 20px 24px;
  864. border-top: 1px solid #e0e0e0;
  865. background: #f8f9fa;
  866. justify-content: flex-end;
  867. }
  868. .btn-agree {
  869. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  870. color: white;
  871. border: none;
  872. padding: 12px 24px;
  873. border-radius: 8px;
  874. cursor: pointer;
  875. font-weight: 500;
  876. transition: all 0.3s ease;
  877. }
  878. .btn-agree:hover {
  879. transform: translateY(-2px);
  880. box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
  881. }
  882. .btn-disagree {
  883. background: #f5f5f5;
  884. color: #666;
  885. border: none;
  886. padding: 12px 24px;
  887. border-radius: 8px;
  888. cursor: pointer;
  889. font-weight: 500;
  890. transition: all 0.3s ease;
  891. }
  892. .btn-disagree:hover {
  893. background: #e0e0e0;
  894. }
  895. </style>