join.vue 30 KB

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