detail.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <template>
  2. <div class="modern-item-add">
  3. <!-- 메인 컨텐츠 -->
  4. <div class="main-content">
  5. <div class="form-container">
  6. <v-form ref="addForm" class="modern-form">
  7. <div class="form-section">
  8. <h3 class="section-title">
  9. <i class="mdi mdi-package-variant"></i>
  10. {{ pageId }}
  11. </h3>
  12. <!-- 제품명 -->
  13. <div class="form-field">
  14. <label class="field-label">
  15. 제품명
  16. </label>
  17. <div class="field-content">
  18. <div class="display-value">
  19. {{ form.formValue1 }}
  20. </div>
  21. </div>
  22. </div>
  23. <div class="form-field">
  24. <label class="field-label">
  25. 판매 기간
  26. </label>
  27. <div class="field-content df--type w--50">
  28. <div class="display-value date-range">
  29. <i class="mdi mdi-calendar-range"></i>
  30. {{ form.order_start_date?.slice(0, 10) }} ~ {{ form.order_end_date?.slice(0, 10) }}
  31. </div>
  32. <v-btn
  33. v-if="form.formValue8 == 0 && memberType !== 'INFLUENCER'"
  34. class="closed-btn"
  35. color="primary"
  36. variant="outlined"
  37. @click="fnCloseEvt"
  38. >
  39. <i class="mdi mdi-power"></i>
  40. 마감
  41. </v-btn>
  42. </div>
  43. </div>
  44. <!-- 인플루언서 (공동구매인 경우) -->
  45. <div class="form-field-group group--2" v-if="memberType !== 'INFLUENCER'">
  46. <div class="form-field">
  47. <label class="field-label">인플루언서</label>
  48. <div class="field-content">
  49. <div class="display-value date-range">
  50. <i class="mdi mdi-account-circle"></i>
  51. {{ form.contact_inf_display }}
  52. </div>
  53. </div>
  54. </div>
  55. <!-- 브랜드사 (공동구매인 경우) -->
  56. <div v-if="memberType !== 'BRAND'" class="form-field">
  57. <label class="field-label">브랜드사</label>
  58. <div class="field-content">
  59. <div class="display-value date-range">
  60. <i class="mdi mdi-domain"></i>
  61. {{ form.contact_brd_display }}
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. <div class="form-field" v-if="form.order_link">
  67. <label class="field-label">구매 링크</label>
  68. <div class="field-content">
  69. <div v-if="pageType == 'D'" class="display-value link-display">
  70. <a
  71. :href="form.order_link"
  72. target="_blank"
  73. rel="noopener noreferrer"
  74. class="external-link"
  75. >
  76. <i class="mdi mdi-link"></i>
  77. {{ form.order_link }}
  78. <i class="mdi mdi-open-in-new"></i>
  79. </a>
  80. </div>
  81. <v-text-field
  82. v-else
  83. v-model="form.order_link"
  84. class="modern-input"
  85. variant="outlined"
  86. placeholder="공동구매 링크를 입력하세요"
  87. maxlength="200"
  88. hide-details="auto"
  89. ></v-text-field>
  90. </div>
  91. </div>
  92. </div>
  93. </v-form>
  94. </div>
  95. <!-- 액션 버튼 -->
  96. <div class="action-buttons">
  97. <div class="button-group left">
  98. <v-btn
  99. class="action-btn secondary"
  100. variant="outlined"
  101. @click="listLocated"
  102. >
  103. <i class="mdi mdi-format-list-bulleted"></i>
  104. 목록으로
  105. </v-btn>
  106. <v-btn
  107. v-if="memberType !== 'INFLUENCER'"
  108. class="action-btn danger"
  109. variant="outlined"
  110. color="error"
  111. @click="fnDelEvt"
  112. >
  113. <i class="mdi mdi-delete"></i>
  114. 삭제
  115. </v-btn>
  116. </div>
  117. <div class="button-group right">
  118. <v-btn
  119. v-if="memberType !== 'INFLUENCER'"
  120. class="action-btn primary"
  121. color="primary"
  122. @click="fnBtnEvt"
  123. >
  124. <i class="mdi mdi-pencil"></i>
  125. 수정하기
  126. </v-btn>
  127. </div>
  128. </div>
  129. </div>
  130. </div>
  131. </template>
  132. <script setup>
  133. import useAxios from "@/composables/useAxios";
  134. import "@vuepic/vue-datepicker/dist/main.css";
  135. /************************************************************************
  136. | 레이아웃
  137. ************************************************************************/
  138. definePageMeta({
  139. layout: "default",
  140. });
  141. /************************************************************************
  142. | 스토어
  143. ************************************************************************/
  144. const useDtStore = useDetailStore();
  145. const useAtStore = useAuthStore();
  146. /************************************************************************
  147. | 전역
  148. ************************************************************************/
  149. const memberType = useAtStore.auth.memberType;
  150. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
  151. const router = useRouter();
  152. const pageId = ref("");
  153. const itemType = useDtStore.boardInfo.itemType;
  154. const datePickerFormat = "yyyy-MM-dd";
  155. const sunEditorWrapper = ref(null); //에디터용 전역
  156. const updatedContent = ref(null); //에디터용 전역
  157. const editorContentReq = ref(); //에디터용 전역
  158. const addForm = ref(null);
  159. const index = ref(null);
  160. const imageIndex = ref(0);
  161. const items = ref([]);
  162. const quillEditor = ref(null);
  163. const imgTemp = ref("");
  164. const zipInfo = ref({
  165. file_path: "",
  166. original_name: ""
  167. })
  168. const rowId = ref();
  169. const form = ref({
  170. formValue1: "",
  171. formValue2: "",
  172. formValue3: "",
  173. formValue4: "",
  174. formValue5: null,
  175. formValue6: "",
  176. formValue7: null,
  177. formValue8: "0",
  178. formValue8Arr: [
  179. { title: "판매중", value: "0" },
  180. { title: "품절", value: "1" },
  181. ],
  182. formValue9: "Y",
  183. formValue9Arr: [
  184. { title: "노출", value: "Y" },
  185. { title: "비노출", value: "N" },
  186. ],
  187. formValue10: "",
  188. contact_inf: "", // 실제 전송될 INFLUENCER_SEQ
  189. contact_inf_display: "", // 화면에 표시될 이름
  190. contact_brd: "", // 실제 전송될 contact_brd
  191. contact_brd_display: "", // 화면에 표시될 브랜드명
  192. order_link: "",
  193. order_start_date: "",
  194. order_end_date: "",
  195. });
  196. // 인플루언서 관련 변수
  197. const influencerModal = ref(false);
  198. const influencerList = ref([]);
  199. const filteredInfluencerList = ref([]);
  200. const selectedInfluencer = ref(null);
  201. const influencerSearchQuery = ref("");
  202. const isSearching = ref(false);
  203. // 브랜드사 관련 변수
  204. const brandModal = ref(false);
  205. const brandList = ref([]);
  206. const filteredBrandList = ref([]);
  207. const selectedBrand = ref(null);
  208. const brandSearchQuery = ref("");
  209. const isBrandSearching = ref(false);
  210. const apiUrl = ref("");
  211. apiUrl.value = import.meta.env.VITE_APP_API_URL;
  212. const objProc = ref({
  213. validErrorMessage: "",
  214. });
  215. const pageType = ref("");
  216. /************************************************************************
  217. | 함수(METHODS)
  218. ************************************************************************/
  219. const listLocated = () => {
  220. router.push({
  221. path: "/view/common/item",
  222. });
  223. useDtStore.boardInfo.itemType = itemType;
  224. };
  225. // 인플루언서 이름 조회
  226. const getInfName = async (contact_inf) => {
  227. try {
  228. // contact_inf 값이 있을 때만 API 호출
  229. if (!contact_inf) {
  230. return null;
  231. }
  232. const response = await useAxios().get(`/user/getInfName/${contact_inf}`);
  233. if (response.data?.status === 'success') {
  234. return response.data.data;
  235. } else {
  236. return null;
  237. }
  238. } catch (error) {
  239. return null;
  240. }
  241. };
  242. // 브랜드사 조회
  243. const getBrdName = async (contact_brd) => {
  244. try {
  245. if (!contact_brd) {
  246. return null;
  247. }
  248. const response = await useAxios().get(`/user/getBrdName/${contact_brd}`);
  249. if (response.data?.status === 'success') {
  250. return response.data.data;
  251. } else {
  252. return null;
  253. }
  254. } catch (error) {
  255. return null;
  256. }
  257. };
  258. /*======================================================================
  259. | 작성 시퀀스
  260. | 1. 작성 컨펌
  261. | 2. 버튼 체크
  262. | 3. 등록시 -> 등록 API 호출
  263. ======================================================================*/
  264. const fnBtnEvt = () => {
  265. //await editorContent();
  266. router.push({
  267. path: "/view/common/item/add",
  268. });
  269. useDtStore.boardInfo.itemType = itemType;
  270. useDtStore.boardInfo.pageType = "U";
  271. };
  272. const fnDelEvt = () => {
  273. let param = {
  274. id: pageId,
  275. title: pageId,
  276. content: "삭제하시겠습니까?",
  277. yes: {
  278. text: "확인",
  279. isProc: true,
  280. event: "FN_DELETE",
  281. param: "",
  282. },
  283. no: {
  284. text: "취소",
  285. isProc: false,
  286. },
  287. };
  288. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  289. };
  290. const fnCloseEvt = () => {
  291. let param = {
  292. id: pageId,
  293. title: pageId,
  294. content: "마감하시겠습니까?",
  295. yes: {
  296. text: "확인",
  297. isProc: true,
  298. event: "FN_CLOSE",
  299. param: "",
  300. },
  301. no: {
  302. text: "취소",
  303. isProc: false,
  304. },
  305. };
  306. $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
  307. };
  308. const fnClose = () => {
  309. let req = {
  310. seq: useDtStore.boardInfo.seq,
  311. };
  312. useAxios()
  313. .post(`/item/close/${req.seq}`)
  314. .then((res) => {
  315. router.push("/view/common/item");
  316. })
  317. .catch((error) => {
  318. })
  319. .finally(() => {
  320. });
  321. };
  322. const fnDelete = () => {
  323. let req = {
  324. seq: useDtStore.boardInfo.seq,
  325. };
  326. useAxios()
  327. .post(`/item/delete/${req.seq}`)
  328. .then((res) => {
  329. router.push("/view/common/item");
  330. })
  331. .catch((error) => {
  332. })
  333. .finally(() => {
  334. });
  335. };
  336. const fnDetail = () => {
  337. let req = {
  338. seq: useDtStore.boardInfo.seq,
  339. };
  340. useAxios()
  341. .get(`/item/detail/${req.seq}`)
  342. .then(async (res) => {
  343. form.value.formValue1 = res.data.NAME;
  344. form.value.formValue8 = res.data.STATUS;
  345. form.value.formValue9 = res.data.SHOW_YN;
  346. form.value.formValue10 = res.data.ADD_INFO;
  347. form.value.order_link = res.data.ORDER_LINK;
  348. form.value.order_start_date = res.data.ORDER_START_DATE;
  349. form.value.order_end_date = res.data.ORDER_END_DATE;
  350. form.value.contact_inf = res.data.CONTACT_INF;
  351. form.value.contact_brd = res.data.CONTACT_BRD;
  352. // contact_inf 값이 있으면 인플루언서 이름 조회
  353. if (res.data.CONTACT_INF) {
  354. const infData = await getInfName(res.data.CONTACT_INF);
  355. if (infData) {
  356. form.value.contact_inf_display = infData.NICK_NAME
  357. ? `${infData.NICK_NAME} (${infData.NAME})`
  358. : infData.NAME;
  359. }
  360. }
  361. if (res.data.CONTACT_BRD) {
  362. const brdData = await getBrdName(res.data.CONTACT_BRD);
  363. if (brdData) {
  364. form.value.contact_brd_display = brdData.NAME;
  365. }
  366. }
  367. })
  368. .catch((error) => {
  369. })
  370. .finally(() => {
  371. });
  372. };
  373. /************************************************************************
  374. | 팝업 이벤트버스 정의
  375. ************************************************************************/
  376. $eventBus.off("FN_DELETE");
  377. $eventBus.on("FN_DELETE", () => {
  378. fnDelete();
  379. });
  380. $eventBus.off("FN_CLOSE");
  381. $eventBus.on("FN_CLOSE", () => {
  382. fnClose();
  383. });
  384. /************************************************************************
  385. | 라이프사이클
  386. ************************************************************************/
  387. onMounted(() => {
  388. pageType.value = "D";
  389. if(pageType.value == "I"){
  390. if(itemType == "G"){
  391. pageId.value = "공동구매 등록"
  392. } else {
  393. pageId.value = "제품 등록"
  394. }
  395. } else if(pageType.value == "U"){
  396. if(itemType == "G"){
  397. pageId.value = "공동구매 수정"
  398. } else {
  399. pageId.value = "제품 수정"
  400. }
  401. } else {
  402. if(itemType == "G"){
  403. pageId.value = "공동구매 상세"
  404. } else {
  405. pageId.value = "제품 상세"
  406. }
  407. }
  408. //상세 등록 아니 리스트 클릭시 상세 정보로 접근
  409. if (pageType.value !== "I") {
  410. fnDetail();
  411. }
  412. });
  413. </script>
  414. <style scoped>
  415. .cursor-pointer {
  416. cursor: pointer;
  417. }
  418. .cursor-pointer:hover {
  419. background-color: #f5f5f5;
  420. }
  421. .order-link {
  422. color: #1976d2;
  423. text-decoration: none;
  424. display: inline-flex;
  425. align-items: center;
  426. transition: color 0.2s;
  427. }
  428. .order-link:hover {
  429. color: #1565c0;
  430. text-decoration: underline;
  431. }
  432. .no-link {
  433. color: #999;
  434. font-style: italic;
  435. }
  436. </style>