newsAdd.vue 26 KB

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