add.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. <template>
  2. <div>
  3. <div class="inner--headers">
  4. <h2>{{ pageId }}</h2>
  5. <div class="bread--crumbs--wrap">
  6. <span>홈</span>
  7. <span>제품 관리</span>
  8. <span>{{ pageId }}</span>
  9. </div>
  10. </div>
  11. <div class="data--list--wrap">
  12. <div class="table--wrap">
  13. <v-form ref="addForm">
  14. <table>
  15. <colgroup>
  16. <col width="20%" />
  17. <col width="80%" />
  18. </colgroup>
  19. <tbody>
  20. <tr>
  21. <th class="bg le">제품명<span v-if="pageType !== 'D'" class="bul">*</span></th>
  22. <td v-if="pageType == 'D'">
  23. {{ form.formValue1 }}
  24. </td>
  25. <td v-else>
  26. <div class="input--wrap">
  27. <v-text-field
  28. maxlength="50"
  29. v-model="form.formValue1"
  30. :rules="[useValid.required('제품명')]"
  31. class="custom-input mini left"
  32. placeholder="제품명을 입력하세요"
  33. ></v-text-field>
  34. </div>
  35. </td>
  36. </tr>
  37. <tr v-if="itemType == 'G'">
  38. <th class="bg le">인플루언서<span v-if="pageType !== 'D'" class="bul">*</span></th>
  39. <td>
  40. <v-text-field
  41. maxlength="50"
  42. v-model="form.contact_inf"
  43. style="width: 20%"
  44. :rules="[useValid.required('인플루언서')]"
  45. readonly=""
  46. class="custom-input mini left"
  47. placeholder="인플루언서를 입력하세요"
  48. ></v-text-field>
  49. </td>
  50. </tr>
  51. <tr v-if="itemType == 'G'">
  52. <th class="bg le">공동구매 기간<span v-if="pageType !== 'D'" class="bul">*</span></th>
  53. <td>
  54. <div class="search--inner">
  55. <div class="calendar-wrap ml--0">
  56. <div class="calendar">
  57. <VueDatePicker
  58. :format="datePickerFormat"
  59. v-model="orderStartDate"
  60. placeholder="날짜를 선택하세요"
  61. :auto-apply="true"
  62. week-start="0"
  63. ></VueDatePicker>
  64. </div>
  65. <span class="text">~</span>
  66. <div class="calendar">
  67. <VueDatePicker
  68. v-model="orderEndDate"
  69. :format="datePickerFormat"
  70. placeholder="날짜를 선택하세요"
  71. :auto-apply="true"
  72. week-start="0"
  73. :min-date="orderStartDate"
  74. ></VueDatePicker>
  75. </div>
  76. </div>
  77. </div>
  78. </td>
  79. </tr>
  80. <tr>
  81. <th class="bg le">공급가<span v-if="pageType !== 'D'" class="bul">*</span></th>
  82. <td v-if="pageType == 'D'">
  83. {{ Number(form.formValue2).toLocaleString() }}
  84. </td>
  85. <td v-else>
  86. <div class="input--wrap">
  87. <v-text-field
  88. maxlength="50"
  89. v-model="form.formValue2"
  90. style="width: 20%"
  91. :rules="[useValid.required('공급가')]"
  92. class="custom-input mini left"
  93. placeholder="공급가를 입력하세요"
  94. ></v-text-field>
  95. </div>
  96. </td>
  97. </tr>
  98. <tr>
  99. <th class="bg le">판매가<span v-if="pageType !== 'D'" class="bul">*</span></th>
  100. <td v-if="pageType == 'D'">
  101. {{ Number(form.formValue3).toLocaleString() }}
  102. </td>
  103. <td v-else>
  104. <div class="input--wrap">
  105. <v-text-field
  106. maxlength="50"
  107. v-model="form.formValue3"
  108. style="width: 20%"
  109. :rules="[useValid.required('판매가')]"
  110. class="custom-input mini left"
  111. placeholder="판매가를 입력하세요"
  112. ></v-text-field>
  113. </div>
  114. </td>
  115. </tr>
  116. <tr>
  117. <th class="bg le">배송비<span v-if="pageType !== 'D'" class="bul">*</span></th>
  118. <td v-if="pageType == 'D'">
  119. {{ form.formValue4 }}
  120. </td>
  121. <td v-else>
  122. <div class="input--wrap">
  123. <v-textarea
  124. v-model="form.formValue4"
  125. class="custom-textarea"
  126. no-resize=""
  127. placeholder="배송비를 입력해주세요"
  128. :rules="[useValid.required('배송비')]"
  129. ></v-textarea>
  130. </div>
  131. </td>
  132. </tr>
  133. <tr v-if="itemType == 'G'">
  134. <th class="bg le">공동구매 링크</th>
  135. <td>
  136. <div class="input--wrap">
  137. <v-text-field
  138. maxlength="50"
  139. v-model="form.order_link"
  140. style="width: 100%"
  141. class="custom-input mini left"
  142. placeholder="공동구매 링크를 입력하세요"
  143. ></v-text-field>
  144. </div>
  145. </td>
  146. </tr>
  147. <tr>
  148. <th class="bg le">썸네일 이미지</th>
  149. <td>
  150. <div class="equip--image--wrap">
  151. <!--이미지가 없을 때-->
  152. <div class="equip--image" v-show="!form.formValue5">
  153. <img src="/assets/img/ic_no_img.svg" />
  154. </div>
  155. <!--이미지 첨부했을 때-->
  156. <div class="equip--image" v-show="form.formValue5">
  157. <div class="images-wrapper">
  158. <img id="preview_image" :src="imgTemp" />
  159. </div>
  160. </div>
  161. <div class="equip--image--select" v-if="pageType !== 'D'">
  162. <div class="form--group">
  163. <label
  164. for="fileUpload_pic"
  165. class="file--btn"
  166. @click="fnPicFileUploadOpen()"
  167. >파일 선택</label
  168. >
  169. <v-file-input
  170. v-model="form.formValue5"
  171. id="fileUpload_pic"
  172. ref="fileupload_pic"
  173. accept=".jpg, .jpeg, .png, .gif"
  174. variant="plain"
  175. hide-details
  176. placeholder="선택된 파일 없음"
  177. prepend-icon=""
  178. class="custom-input"
  179. style="max-width: 400px"
  180. height="33px"
  181. :clearable="false"
  182. @change="fnUploadPicFileCheck()"
  183. >
  184. <!-- <template #append>
  185. <div class="v-input__icon v-input__icon--clear">
  186. <button
  187. @click="clearFile"
  188. type="button"
  189. aria-label="clear icon"
  190. tabindex="-1"
  191. class="v-icon notranslate v-icon--link mdi mdi-close"
  192. ></button>
  193. </div>
  194. </template> -->
  195. </v-file-input>
  196. </div>
  197. <p class="equip--image--desc">
  198. (권장 이미지 : gif, jpg, jpeg, png)
  199. </p>
  200. </div>
  201. </div>
  202. </td>
  203. </tr>
  204. <tr>
  205. <th class="bg le">소타이틀<span v-if="pageType !== 'D'" class="bul">*</span></th>
  206. <td v-if="pageType == 'D'">
  207. {{ form.formValue6 }}
  208. </td>
  209. <td v-else>
  210. <div class="input--wrap">
  211. <v-text-field
  212. v-model="form.formValue6"
  213. class="custom-input mini"
  214. placeholder="소타이틀을 입력해주세요"
  215. :rules="[useValid.required('소타이틀')]"
  216. ></v-text-field>
  217. </div>
  218. </td>
  219. </tr>
  220. <tr>
  221. <th class="bg le">상세 다운로드</th>
  222. <td>
  223. <div class="input--wrap" style="width: 50%">
  224. <v-file-input
  225. v-if="pageType !== 'D'"
  226. v-model="form.formValue7"
  227. label="파일은 압축(zip)해서 첨부해 주세요."
  228. accept=".zip"
  229. variant="outlined"
  230. hide-details=""
  231. density="comfortable"
  232. ></v-file-input>
  233. <div class="down--file" @click="fnDownloadFile()">
  234. <span>{{ zipInfo.original_name }}</span>
  235. </div>
  236. </div>
  237. </td>
  238. </tr>
  239. <tr>
  240. <th class="bg le">상세 내용<span v-if="pageType !== 'D'" class="bul">*</span></th>
  241. <td v-if="pageType == 'D'">
  242. <div v-html="editorContentReq"></div>
  243. </td>
  244. <td v-else>
  245. <SunEditorWrapper
  246. ref="sunEditorWrapper"
  247. :initialContent="editorContentReq"
  248. />
  249. </td>
  250. </tr>
  251. <tr>
  252. <th class="bg le">상태<span v-if="pageType !== 'D'" class="bul">*</span></th>
  253. <td v-if="pageType == 'D'">
  254. {{ form.formValue8 == '0' ? '판매중' : '품절' }}
  255. </td>
  256. <td v-else>
  257. <div class="input--wrap" style="width: 20%">
  258. <v-select
  259. variant="outlined"
  260. class="custom-select"
  261. v-model="form.formValue8"
  262. :items="form.formValue8Arr"
  263. >
  264. </v-select>
  265. </div>
  266. </td>
  267. </tr>
  268. <tr v-if="pageType !== 'D'">
  269. <th class="bg le">노출 상태<span v-if="pageType !== 'D'" class="bul">*</span></th>
  270. <td>
  271. <div class="input--wrap" style="width: 20%">
  272. <v-select
  273. variant="outlined"
  274. style="width: 20%"
  275. class="custom-select"
  276. v-model="form.formValue9"
  277. :items="form.formValue9Arr"
  278. >
  279. </v-select>
  280. </div>
  281. </td>
  282. </tr>
  283. <tr>
  284. <th class="bg le">업데이트 내역</th>
  285. <td v-if="pageType == 'D'">
  286. {{ form.formValue10 }}
  287. </td>
  288. <td v-else>
  289. <div class="input--wrap">
  290. <v-textarea
  291. v-model="form.formValue10"
  292. class="custom-textarea"
  293. no-resize=""
  294. placeholder="업데이트 내역을 입력해주세요"
  295. ></v-textarea>
  296. </div>
  297. </td>
  298. </tr>
  299. </tbody>
  300. </table>
  301. </v-form>
  302. </div>
  303. <div class="view-btm-btn">
  304. <div class="btn-l">
  305. <v-btn class="custom-btn btn-list" @click="listLocated"
  306. ><i class="ico"></i>목록</v-btn
  307. >
  308. <v-btn v-show="pageType == 'U'" class="custom-btn btn-del" @click="fnDelEvt"
  309. ><i class="ico"></i>삭제</v-btn
  310. >
  311. </div>
  312. <div class="btn-r">
  313. <v-btn v-if="pageType !== 'D'" class="custom-btn btn-blue2" @click="fnBtnEvt"
  314. ><i class="ico"></i>저장</v-btn
  315. >
  316. </div>
  317. </div>
  318. </div>
  319. </div>
  320. </template>
  321. <script setup>
  322. import SunEditorWrapper from "@/components/sunEdt.vue";
  323. import useAxios from "@/composables/useAxios";
  324. import VueDatePicker from "@vuepic/vue-datepicker";
  325. import "@vuepic/vue-datepicker/dist/main.css";
  326. /************************************************************************
  327. | 레이아웃
  328. ************************************************************************/
  329. definePageMeta({
  330. layout: "default",
  331. });
  332. /************************************************************************
  333. | 스토어
  334. ************************************************************************/
  335. const useDtStore = useDetailStore();
  336. const useAtStore = useAuthStore();
  337. /************************************************************************
  338. | 전역
  339. ************************************************************************/
  340. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
  341. const router = useRouter();
  342. const pageId = ref("");
  343. const itemType = useDtStore.boardInfo.itemType;
  344. const orderStartDate = ref("");
  345. const orderEndDate = ref("");
  346. const datePickerFormat = "yyyy-MM-dd";
  347. const sunEditorWrapper = ref(null); //에디터용 전역
  348. const updatedContent = ref(null); //에디터용 전역
  349. const editorContentReq = ref(); //에디터용 전역
  350. const addForm = ref(null);
  351. const index = ref(null);
  352. const imageIndex = ref(0);
  353. const items = ref([]);
  354. const quillEditor = ref(null);
  355. const imgTemp = ref("");
  356. const zipInfo = ref({
  357. file_path: "",
  358. original_name: ""
  359. })
  360. const rowId = ref();
  361. const form = ref({
  362. formValue1: "",
  363. formValue2: "",
  364. formValue3: "",
  365. formValue4: "",
  366. formValue5: null,
  367. formValue6: "",
  368. formValue7: null,
  369. formValue8: "0",
  370. formValue8Arr: [
  371. { title: "판매중", value: "0" },
  372. { title: "품절", value: "1" },
  373. ],
  374. formValue9: "Y",
  375. formValue9Arr: [
  376. { title: "노출", value: "Y" },
  377. { title: "비노출", value: "N" },
  378. ],
  379. formValue10: "",
  380. contact_inf: "18",
  381. order_link: "",
  382. });
  383. const apiUrl = ref("");
  384. apiUrl.value = import.meta.env.VITE_APP_API_URL;
  385. const objProc = ref({
  386. validErrorMessage: "",
  387. });
  388. const pageType = ref("");
  389. /************************************************************************
  390. | 함수(METHODS)
  391. ************************************************************************/
  392. const listLocated = () => {
  393. router.push({
  394. path: "/view/common/item",
  395. });
  396. };
  397. const fnPicFileUploadOpen = () => {
  398. let fileUpload = document.getElementById("fileupload_pic");
  399. if (fileUpload != null) {
  400. fileUpload.click();
  401. }
  402. };
  403. const fnUploadPicFileCheck = () => {
  404. if (form.value.formValue5) {
  405. // 10Mb 이상은 업로드 불가
  406. if (form.value.formValue5.size > 10 * 1024 * 1024) {
  407. fnOpenCommPop("10mb 이상은 업로드가 불가합니다.");
  408. form.value.formValue5 = null;
  409. return;
  410. }
  411. // 이미지 파일 형식 체크
  412. let extension = form.value.formValue5.name.split(".").pop().toLowerCase();
  413. if (
  414. extension != "jpg" &&
  415. extension != "jpeg" &&
  416. extension != "png" &&
  417. extension != "gif"
  418. ) {
  419. fnOpenCommPop("파일 형식 또는 확장자가 올바르지 않습니다.");
  420. form.value.formValue5 = null;
  421. return;
  422. }
  423. objProc.validErrorMessage = "";
  424. // 이미지 미리보기
  425. let previewImage = new Image();
  426. let tempImageUrl = window.URL.createObjectURL(form.value.formValue5);
  427. //console.log(tempImageUrl);
  428. previewImage.src = tempImageUrl;
  429. items.value[0] = tempImageUrl;
  430. imgTemp.value = tempImageUrl;
  431. }
  432. };
  433. const clearFile = () => {
  434. form.value.formValue5 = null;
  435. };
  436. const fnDownloadFile = () => {
  437. window.location.href = `https://shopdeli.mycafe24.com/item/download/${zipInfo.value.file_path}`;
  438. }
  439. /*======================================================================
  440. | 작성 시퀀스
  441. | 1. 작성 컨펌
  442. | 2. 버튼 체크
  443. | 3. 등록시 -> 등록 API 호출
  444. ======================================================================*/
  445. const fnBtnEvt = async () => {
  446. await editorContent();
  447. nextTick(() => {
  448. if (addForm.value && typeof addForm.value.validate === "function") {
  449. addForm.value
  450. .validate()
  451. .then((isValid) => {
  452. if (
  453. isValid.valid &&
  454. updatedContent.value != undefined &&
  455. updatedContent.value != null &&
  456. updatedContent.value != "<p><br></p>" &&
  457. form.value.formValue4 != null
  458. ) {
  459. if (pageType.value == "I") fnRegEvt();
  460. else fnUpdEvt();
  461. } else {
  462. let param = {
  463. id: pageId,
  464. title: pageId,
  465. content: "필수항목을 입력해주세요.",
  466. yes: {
  467. text: "확인",
  468. isProc: false,
  469. }
  470. };
  471. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  472. }
  473. })
  474. .catch((err) => {
  475. });
  476. } else {
  477. }
  478. });
  479. };
  480. const fnOpenCommPop = (__TEXT) => {
  481. let param = {
  482. id: pageId,
  483. title: "알림",
  484. content: __TEXT,
  485. yes: {
  486. text: "확인",
  487. isProc: false,
  488. },
  489. };
  490. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  491. };
  492. const fnRegEvt = () => {
  493. let param = {
  494. id: pageId,
  495. title: pageId,
  496. content: "등록하시겠습니까?",
  497. yes: {
  498. text: "등록",
  499. isProc: true,
  500. event: "FN_INSERT",
  501. param: "",
  502. },
  503. no: {
  504. text: "취소",
  505. isProc: false,
  506. },
  507. };
  508. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  509. };
  510. const fnUpdEvt = () => {
  511. let param = {
  512. id: pageId,
  513. title: pageId,
  514. content: "수정하시겠습니까?",
  515. yes: {
  516. text: "확인",
  517. isProc: true,
  518. event: "FN_UPDATE",
  519. param: "",
  520. },
  521. no: {
  522. text: "취소",
  523. isProc: false,
  524. },
  525. };
  526. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  527. };
  528. const fnInsert = async () => {
  529. const formData = new FormData();
  530. formData.append('name', form.value.formValue1);
  531. formData.append('price1', form.value.formValue2);
  532. formData.append('price2', form.value.formValue3);
  533. formData.append('deli_fee', form.value.formValue4);
  534. formData.append('thumb_file', form.value.formValue5);
  535. formData.append('sub_title', form.value.formValue6);
  536. formData.append('detail', updatedContent.value);
  537. formData.append('zip_file', form.value.formValue7);
  538. formData.append('status', form.value.formValue8);
  539. formData.append('show_yn', form.value.formValue9);
  540. formData.append('add_info', form.value.formValue10);
  541. formData.append('order_link', form.value.order_link);
  542. formData.append('order_start_date', form.value.order_start_date);
  543. formData.append('order_end_date', form.value.order_end_date);
  544. formData.append('item_type', itemType);
  545. formData.append('contact_inf', 18);
  546. // 벤더사의 COMPANY_NUMBER 사용
  547. const memberCompanyNumber = useAtStore.auth.companyNumber || "1";
  548. formData.append('company_number', memberCompanyNumber);
  549. useAxios()
  550. .post('/item/reg', formData, {
  551. headers: {'Content-Type': 'multipart/form-data'},
  552. })
  553. .then((res) => {
  554. router.push("/view/common/item");
  555. })
  556. .catch((error) => {
  557. })
  558. .finally(() => {
  559. });
  560. };
  561. const fnUpdate = async () => {
  562. let req = {
  563. seq: useDtStore.boardInfo.seq,
  564. };
  565. const formData = new FormData();
  566. formData.append('name', form.value.formValue1);
  567. formData.append('price1', form.value.formValue2);
  568. formData.append('price2', form.value.formValue3);
  569. formData.append('deli_fee', form.value.formValue4);
  570. if (form.value.formValue5 instanceof File) {
  571. formData.append('thumb_file', form.value.formValue5);
  572. }
  573. formData.append('sub_title', form.value.formValue6);
  574. formData.append('detail', updatedContent.value);
  575. if (form.value.formValue7 instanceof File) {
  576. formData.append('zip_file', form.value.formValue7);
  577. }
  578. formData.append('status', form.value.formValue8);
  579. formData.append('show_yn', form.value.formValue9);
  580. formData.append('add_info', form.value.formValue10);
  581. // 벤더사의 COMPANY_NUMBER 사용
  582. const memberCompanyNumber = useAtStore.auth.companyNumber || "1";
  583. formData.append('company_number', memberCompanyNumber);
  584. try {
  585. const res = await useAxios().post(`/item/update/${req.seq}`, formData, {
  586. headers: { 'Content-Type': 'multipart/form-data' },
  587. });
  588. router.push("/view/common/item");
  589. } catch (error) {
  590. }
  591. };
  592. const fnDelEvt = () => {
  593. let param = {
  594. id: pageId,
  595. title: pageId,
  596. content: "삭제하시겠습니까?",
  597. yes: {
  598. text: "확인",
  599. isProc: true,
  600. event: "FN_DELETE",
  601. param: "",
  602. },
  603. no: {
  604. text: "취소",
  605. isProc: false,
  606. },
  607. };
  608. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  609. };
  610. const fnDelete = () => {
  611. let req = {
  612. seq: useDtStore.boardInfo.seq,
  613. };
  614. useAxios()
  615. .post(`/item/delete/${req.seq}`)
  616. .then((res) => {
  617. //router.push("/view/common/item");
  618. })
  619. .catch((error) => {
  620. })
  621. .finally(() => {
  622. });
  623. };
  624. const fnDetail = () => {
  625. let req = {
  626. seq: useDtStore.boardInfo.seq,
  627. };
  628. useAxios()
  629. .get(`/item/detail/${req.seq}`)
  630. .then((res) => {
  631. form.value.formValue1 = res.data.NAME;
  632. form.value.formValue2 = res.data.PRICE1;
  633. form.value.formValue3 = res.data.PRICE2;
  634. form.value.formValue4 = res.data.DELI_FEE;
  635. form.value.formValue5 = res.data.THUMB_FILE;
  636. form.value.formValue6 = res.data.SUB_TITLE;
  637. zipInfo.value.file_path = res.data.ZIP_FILE;
  638. zipInfo.value.original_name = res.data.ZIP_FILE_ORIGIN;
  639. //에디터에 컨텐츠 전달
  640. editorContentReq.value = res.data.DETAIL;
  641. form.value.formValue8 = res.data.STATUS;
  642. form.value.formValue9 = res.data.SHOW_YN;
  643. form.value.formValue10 = res.data.ADD_INFO;
  644. //썸네일 파일이 있으면 넣어줌
  645. if(form.value.formValue5){
  646. imgTemp.value = `https://shopdeli.mycafe24.com/writable/uploads/item/thumb/${form.value.formValue5}`;
  647. }
  648. })
  649. .catch((error) => {
  650. })
  651. .finally(() => {
  652. });
  653. };
  654. /*=======================================================================
  655. | 최종 에디터 이미지 url치환 : S
  656. /*=======================================================================*/
  657. const editorContent = async () => {
  658. const content = sunEditorWrapper.value.getEditorContent();
  659. updatedContent.value = await processEditorContent(content);
  660. console.log("Updated content:", updatedContent.value);
  661. };
  662. // Base64 데이터를 Blob으로 변환
  663. const base64ToBlob = (base64, mimeType) => {
  664. const byteString = atob(base64.split(",")[1]);
  665. const arrayBuffer = new ArrayBuffer(byteString.length);
  666. const uint8Array = new Uint8Array(arrayBuffer);
  667. for (let i = 0; i < byteString.length; i++) {
  668. uint8Array[i] = byteString.charCodeAt(i);
  669. }
  670. return new Blob([uint8Array], { type: mimeType });
  671. };
  672. // Base64 데이터를 File 객체로 변환
  673. const base64ToFile = (base64, mimeType, fileName) => {
  674. const blob = base64ToBlob(base64, mimeType);
  675. return new File([blob], fileName, { type: mimeType });
  676. };
  677. // 이미지 업로드 처리 (useAxios)
  678. const uploadImage = async (file) => {
  679. const formDataEdt = new FormData();
  680. formDataEdt.append("picObj", file);
  681. return useAxios()
  682. .post("/pic/upload", formDataEdt, {
  683. headers: { "Content-Type": "multipart/form-data" },
  684. })
  685. .then((res) => {
  686. const filePath = res.data.ogn_name.path.replace(/.*\/files\//, "");
  687. const fileName = res.data.ogn_name.file_name;
  688. return `${apiUrl.value}/images/${filePath}/${fileName}`; // 최종 URL 반환
  689. })
  690. .catch((error) => {
  691. console.error("Image upload failed:", error);
  692. return null;
  693. });
  694. };
  695. // 에디터 내용 처리 및 이미지 업로드
  696. const processEditorContent = async (content) => {
  697. const parser = new DOMParser();
  698. const doc = parser.parseFromString(content, "text/html");
  699. const images = doc.querySelectorAll("img");
  700. for (let i = 0; i < images.length; i++) {
  701. const img = images[i];
  702. const src = img.src;
  703. if (src.startsWith("data:image")) {
  704. // MIME 타입과 파일 이름 추출
  705. const mimeType = src.split(";")[0].split(":")[1];
  706. const extension = mimeType.split("/")[1];
  707. const fileName = `image-${i + 1}.${extension}`;
  708. // Base64 데이터를 File 객체로 변환
  709. const file = base64ToFile(src, mimeType, fileName);
  710. // 이미지 업로드 및 URL 반환
  711. const finalUrl = await uploadImage(file);
  712. if (finalUrl) {
  713. img.src = finalUrl; // 이미지 src 업데이트
  714. }
  715. }
  716. }
  717. return doc.body.innerHTML; // 최종 수정된 HTML 반환
  718. };
  719. /*=======================================================================
  720. | 최종 에디터 이미지 url치환 : E
  721. /*=======================================================================*/
  722. /************************************************************************
  723. | 팝업 이벤트버스 정의
  724. ************************************************************************/
  725. $eventBus.off("FN_INSERT");
  726. $eventBus.on("FN_INSERT", () => {
  727. fnInsert();
  728. });
  729. $eventBus.off("FN_UPDATE");
  730. $eventBus.on("FN_UPDATE", () => {
  731. fnUpdate();
  732. });
  733. $eventBus.off("FN_DELETE");
  734. $eventBus.on("FN_DELETE", () => {
  735. fnDelete();
  736. });
  737. /************************************************************************
  738. | 라이프사이클
  739. ************************************************************************/
  740. onMounted(() => {
  741. pageType.value = useDtStore.boardInfo.pageType;
  742. if(pageType.value == "I"){
  743. if(itemType == "G"){
  744. pageId.value = "공동구매 제품 등록"
  745. } else {
  746. pageId.value = "제품 등록"
  747. }
  748. } else if(pageType.value == "U"){
  749. if(itemType == "G"){
  750. pageId.value = "공동구매 제품 수정"
  751. } else {
  752. pageId.value = "제품 수정"
  753. }
  754. } else {
  755. if(itemType == "G"){
  756. pageId.value = "공동구매 제품 상세"
  757. } else {
  758. pageId.value = "제품 상세"
  759. }
  760. }
  761. //상세 등록 아니 리스트 클릭시 상세 정보로 접근
  762. if (pageType.value !== "I") {
  763. fnDetail();
  764. }
  765. });
  766. /************************************************************************
  767. | WATCH
  768. ************************************************************************/
  769. // 시작일이 변경될 때, 종료일이 시작일보다 이전이면 종료일을 시작일과 같게 설정
  770. watch(orderStartDate, (newStartDate) => {
  771. if (newStartDate && orderEndDate.value && orderEndDate.value < newStartDate) {
  772. orderEndDate.value = newStartDate;
  773. }
  774. });
  775. // 종료일이 변경될 때, 종료일이 시작일보다 이전이면 시작일과 같게 설정
  776. watch(orderEndDate, (newEndDate) => {
  777. if (newEndDate && orderStartDate.value && newEndDate < orderStartDate.value) {
  778. orderEndDate.value = orderStartDate.value;
  779. }
  780. });
  781. </script>