| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164 |
- <template>
- <div class="modern-item-add">
- <!-- 헤더 섹션 -->
- <!-- <div class="page-header">
- <div class="header-content">
- <div class="title-section">
- <h1 class="page-title">{{ pageId }}</h1>
- </div>
- <div class="breadcrumb-section">
- <div class="breadcrumb">
- <span class="breadcrumb-item">홈</span>
- <i class="mdi mdi-chevron-right breadcrumb-divider"></i>
- <span class="breadcrumb-item">공동구매</span>
- <i class="mdi mdi-chevron-right breadcrumb-divider"></i>
- <span class="breadcrumb-item active">{{ pageId }}</span>
- </div>
- </div>
- </div>
- </div> -->
- <!-- 메인 컨텐츠 -->
- <div class="main-content">
- <div class="form-container">
- <v-form ref="addForm" class="modern-form">
- <div class="form-section">
- <h3 class="section-title">
- <i class="mdi mdi-package-variant"></i>
- {{ pageId }}
- </h3>
-
- <!-- 제품명 -->
- <div class="form-field">
- <label class="field-label">
- 제품명
- <span v-if="pageType !== 'D'" class="required-mark">*</span>
- </label>
- <div class="field-content">
- <div v-if="pageType == 'D'" class="display-value">
- {{ form.formValue1 }}
- </div>
- <v-text-field
- v-else
- v-model="form.formValue1"
- :rules="[useValid.required('제품명')]"
- class="modern-input"
- variant="outlined"
- placeholder="제품명을 입력하세요"
- maxlength="50"
- hide-details="auto"
- ></v-text-field>
- </div>
- </div>
- <!-- 기간 (공동구매인 경우) -->
- <div v-if="itemType == 'G'" class="form-field">
- <label class="field-label">
- 판매 기간
- <span v-if="pageType !== 'D'" class="required-mark">*</span>
- </label>
- <div class="field-content">
- <div v-if="pageType == 'D'" class="display-value date-range">
- <i class="mdi mdi-calendar-range"></i>
- {{ form.order_start_date?.slice(0, 10) }} ~ {{ form.order_end_date?.slice(0, 10) }}
- </div>
- <div v-else class="date-picker-wrapper">
- <div class="date-picker-group">
- <div class="date-picker-item">
- <VueDatePicker
- v-model="form.order_start_date"
- :format="datePickerFormat"
- placeholder="시작 날짜"
- :auto-apply="true"
- week-start="0"
- class="modern-date-picker"
- />
- </div>
- <span class="date-separator">~</span>
- <div class="date-picker-item">
- <VueDatePicker
- v-model="form.order_end_date"
- :format="datePickerFormat"
- placeholder="종료 날짜"
- :auto-apply="true"
- week-start="0"
- :min-date="form.order_start_date"
- class="modern-date-picker"
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- 인플루언서 (공동구매인 경우) -->
- <div class="form-field-group group--2">
- <div v-if="itemType == 'G' && pageType !== 'D'" class="form-field">
- <label class="field-label">인플루언서</label>
- <div class="field-content">
- <div class="selector-wrapper">
- <v-text-field
- v-model="form.contact_inf_display"
- class="modern-input selector-input"
- variant="outlined"
- placeholder="인플루언서를 선택하세요"
- readonly
- hide-details="auto"
- ></v-text-field>
- <v-btn
- class="selector-btn"
- color="primary"
- variant="outlined"
- @click="openInfluencerModal"
- >
- <i class="mdi mdi-account-search"></i>
- 선택
- </v-btn>
- </div>
- </div>
- </div>
- <!-- 브랜드사 (공동구매인 경우) -->
- <div v-if="itemType == 'G' && pageType !== 'D' && memberType !== 'BRAND'" class="form-field">
- <label class="field-label">브랜드사</label>
- <div class="field-content">
- <div class="selector-wrapper">
- <v-text-field
- v-model="form.contact_brd_display"
- class="modern-input selector-input"
- variant="outlined"
- placeholder="브랜드사를 선택하세요"
- readonly
- hide-details="auto"
- ></v-text-field>
- <v-btn
- class="selector-btn"
- color="primary"
- variant="outlined"
- @click="openBrandModal"
- >
- <i class="mdi mdi-domain"></i>
- 선택
- </v-btn>
- </div>
- </div>
- </div>
- </div>
- <!-- 구매 링크 (공동구매인 경우) -->
- <div v-if="itemType == 'G'" class="form-field">
- <label class="field-label">구매 링크</label>
- <div class="field-content">
- <div v-if="pageType == 'D'" class="display-value link-display">
- <a
- v-if="form.order_link"
- :href="form.order_link"
- target="_blank"
- rel="noopener noreferrer"
- class="external-link"
- >
- <i class="mdi mdi-link"></i>
- {{ form.order_link }}
- <i class="mdi mdi-open-in-new"></i>
- </a>
- <span v-else class="no-data">링크가 설정되지 않았습니다</span>
- </div>
- <v-text-field
- v-else
- v-model="form.order_link"
- class="modern-input"
- variant="outlined"
- placeholder="공동구매 링크를 입력하세요"
- maxlength="200"
- hide-details="auto"
- ></v-text-field>
- </div>
- </div>
- </div>
- </v-form>
- </div>
- <!-- 액션 버튼 -->
- <div class="action-buttons">
- <div class="button-group left">
- <v-btn
- v-if="pageType =='I'"
- class="action-btn secondary"
- variant="outlined"
- @click="listLocated"
- >
- <i class="mdi mdi-format-list-bulleted"></i>
- 목록으로
- </v-btn>
- <v-btn
- v-if="pageType =='U'"
- class="action-btn secondary"
- variant="outlined"
- @click="detailLocated"
- >
- <i class="mdi mdi-refresh"></i>
- 돌아가기
- </v-btn>
- <!-- <v-btn
- v-show="pageType == 'U'"
- class="action-btn danger"
- variant="outlined"
- color="error"
- @click="fnDelEvt"
- >
- <i class="mdi mdi-delete"></i>
- 삭제
- </v-btn> -->
- </div>
- <div class="button-group right">
- <v-btn
- v-if="pageType !== 'D'"
- class="action-btn primary"
- color="primary"
- @click="fnBtnEvt"
- >
- <i class="mdi mdi-content-save"></i>
- 저장하기
- </v-btn>
- </div>
- </div>
- </div>
- <!-- 인플루언서 선택 모달 -->
- <v-dialog v-model="influencerModal" max-width="600px" persistent class="modern-modal">
- <v-card class="modal-card influencer-modal-card">
- <v-card-title class="modal-header">
- <div class="modal-title">
- <i class="mdi mdi-account-star"></i>
- 인플루언서 선택
- </div>
- <v-btn
- icon
- variant="text"
- @click="closeInfluencerModal"
- class="close-btn"
- >
- <i class="mdi mdi-close"></i>
- </v-btn>
- </v-card-title>
-
- <v-card-text class="modal-content">
- <!-- 검색 영역 -->
- <div class="search-section">
- <v-text-field
- v-model="influencerSearchQuery"
- class="search-input"
- variant="outlined"
- placeholder="이름 또는 닉네임으로 검색"
- prepend-inner-icon="mdi-magnify"
- clearable
- hide-details
- @input="searchInfluencers"
- >
- <template v-slot:append-inner>
- <v-progress-circular
- v-if="isSearching"
- indeterminate
- size="20"
- width="2"
- color="primary"
- ></v-progress-circular>
- </template>
- </v-text-field>
- </div>
- <!-- 결과 영역 -->
- <div v-if="filteredInfluencerList.length === 0 && !isSearching" class="empty-state">
- <i class="mdi mdi-account-search-outline"></i>
- <h4>{{ influencerSearchQuery ? '검색 결과가 없습니다' : '등록된 인플루언서가 없습니다' }}</h4>
- <p>{{ influencerSearchQuery ? '다른 검색어로 시도해보세요' : '먼저 인플루언서 등록을 진행해 주세요' }}</p>
- </div>
-
- <!-- 인플루언서 목록 -->
- <div v-else class="influencer-list-container">
- <div
- v-for="influencer in filteredInfluencerList"
- :key="influencer.SEQ"
- @click="selectInfluencer(influencer)"
- class="influencer-list-item"
- :class="{ selected: selectedInfluencer?.SEQ === influencer.SEQ }"
- >
- <div class="item-avatar">
- <i class="mdi mdi-account-circle"></i>
- </div>
- <div class="item-info">
- <h4 class="nickname">{{ influencer.NICK_NAME }} <span class="name">({{ influencer.NAME }})</span></h4>
- <div class="sns-info" v-if="influencer.SNS_TYPE">
- <i class="mdi" :class="getSocialIcon(influencer.SNS_TYPE)"></i>
- <span>{{ influencer.SNS_LINK_ID }}</span>
- </div>
- </div>
- </div>
- </div>
- </v-card-text>
-
- <v-card-actions class="modal-actions">
- </v-card-actions>
- </v-card>
- </v-dialog>
- <!-- 브랜드사 선택 모달 -->
- <v-dialog v-model="brandModal" max-width="600px" persistent class="modern-modal">
- <v-card class="modal-card brand-modal-card">
- <v-card-title class="modal-header">
- <div class="modal-title">
- <i class="mdi mdi-domain"></i>
- 브랜드사 선택
- </div>
- <v-btn
- icon
- variant="text"
- @click="closeBrandModal"
- class="close-btn"
- >
- <i class="mdi mdi-close"></i>
- </v-btn>
- </v-card-title>
-
- <v-card-text class="modal-content">
- <!-- 검색 영역 -->
- <div class="search-section">
- <v-text-field
- v-model="brandSearchQuery"
- class="search-input"
- variant="outlined"
- placeholder="회사명으로 검색"
- prepend-inner-icon="mdi-magnify"
- clearable
- hide-details
- @input="searchBrands"
- >
- <template v-slot:append-inner>
- <v-progress-circular
- v-if="isBrandSearching"
- indeterminate
- size="20"
- width="2"
- color="primary"
- ></v-progress-circular>
- </template>
- </v-text-field>
- </div>
- <!-- 결과 영역 -->
- <div v-if="filteredBrandList.length === 0 && !isBrandSearching" class="empty-state">
- <i class="mdi mdi-domain-off"></i>
- <h4>{{ brandSearchQuery ? '검색 결과가 없습니다' : '등록된 브랜드사가 없습니다' }}</h4>
- <p>{{ brandSearchQuery ? '다른 검색어로 시도해보세요' : '먼저 브랜드사 등록을 진행해 주세요' }}</p>
- </div>
-
- <!-- 브랜드사 목록 -->
- <div v-else class="influencer-list-container">
- <div
- v-for="brand in filteredBrandList"
- :key="brand.SEQ"
- @click="selectBrand(brand)"
- class="influencer-list-item"
- :class="{ selected: selectedBrand?.SEQ === brand.SEQ }"
- >
- <div class="item-avatar">
- <i class="mdi mdi-domain"></i>
- </div>
- <div class="item-info">
- <h4 class="nickname">{{ brand.COMPANY_NAME }}</h4>
- <!-- <p class="name">{{ brand.NAME }}</p> -->
- <!-- <div class="sns-info" v-if="brand.EMAIL">
- <i class="mdi mdi-email"></i>
- <span>{{ brand.EMAIL }}</span>
- </div> -->
- </div>
- </div>
- </div>
- </v-card-text>
-
- <v-card-actions class="modal-actions">
- </v-card-actions>
- </v-card>
- </v-dialog>
- </div>
- </template>
- <script setup>
- import useAxios from "@/composables/useAxios";
- import VueDatePicker from "@vuepic/vue-datepicker";
- import "@vuepic/vue-datepicker/dist/main.css";
- import dayjs from 'dayjs';
- /************************************************************************
- | 레이아웃
- ************************************************************************/
- definePageMeta({
- layout: "default",
- });
- /************************************************************************
- | 스토어
- ************************************************************************/
- const useDtStore = useDetailStore();
- const useAtStore = useAuthStore();
- /************************************************************************
- | 전역
- ************************************************************************/
- const memberType = useAtStore.auth.memberType;
- const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
- const router = useRouter();
- const pageId = ref("");
- const itemType = useDtStore.boardInfo.itemType;
- const datePickerFormat = "yyyy-MM-dd";
- const sunEditorWrapper = ref(null); //에디터용 전역
- const updatedContent = ref(null); //에디터용 전역
- const editorContentReq = ref(); //에디터용 전역
- const addForm = ref(null);
- const index = ref(null);
- const imageIndex = ref(0);
- const items = ref([]);
- const quillEditor = ref(null);
- const imgTemp = ref("");
- const zipInfo = ref({
- file_path: "",
- original_name: ""
- })
- const rowId = ref();
- const form = ref({
- formValue1: "",
- formValue2: "",
- formValue3: "",
- formValue4: "",
- formValue5: null,
- formValue6: "",
- formValue7: null,
- formValue8: "0",
- formValue8Arr: [
- { title: "판매중", value: "0" },
- { title: "품절", value: "1" },
- ],
- formValue9: "Y",
- formValue9Arr: [
- { title: "노출", value: "Y" },
- { title: "비노출", value: "N" },
- ],
- formValue10: "",
- contact_inf: "", // 실제 전송될 INFLUENCER_SEQ
- contact_inf_display: "", // 화면에 표시될 이름
- contact_brd: "", // 실제 전송될 contact_brd
- contact_brd_display: "", // 화면에 표시될 브랜드명
- order_link: "",
- order_start_date: "",
- order_end_date: "",
- });
- // 인플루언서 관련 변수
- const influencerModal = ref(false);
- const influencerList = ref([]);
- const filteredInfluencerList = ref([]);
- const selectedInfluencer = ref(null);
- const influencerSearchQuery = ref("");
- const isSearching = ref(false);
- // 브랜드사 관련 변수
- const brandModal = ref(false);
- const brandList = ref([]);
- const filteredBrandList = ref([]);
- const selectedBrand = ref(null);
- const brandSearchQuery = ref("");
- const isBrandSearching = ref(false);
- const apiUrl = ref("");
- apiUrl.value = import.meta.env.VITE_APP_API_URL;
- const objProc = ref({
- validErrorMessage: "",
- });
- const pageType = ref("");
- /************************************************************************
- | 함수(METHODS)
- ************************************************************************/
- const listLocated = () => {
- router.push({
- path: "/view/common/item",
- });
- useDtStore.boardInfo.itemType = itemType;
- };
- const detailLocated = () => {
- router.push({
- path: "/view/common/item/detail",
- });
- useDtStore.boardInfo.itemType = itemType;
- };
- // 인플루언서 이름 조회
- const getInfName = async (contact_inf) => {
- try {
- // contact_inf 값이 있을 때만 API 호출
- if (!contact_inf) {
- return null;
- }
- const response = await useAxios().get(`/user/getInfName/${contact_inf}`);
-
- if (response.data?.status === 'success') {
- return response.data.data;
- } else {
- return null;
- }
- } catch (error) {
- return null;
- }
- };
- // 브랜드사 조회
- const getBrdName = async (contact_brd) => {
- try {
- if (!contact_brd) {
- return null;
- }
- const response = await useAxios().get(`/user/getBrdName/${contact_brd}`);
-
- if (response.data?.status === 'success') {
- return response.data.data;
- } else {
- return null;
- }
- } catch (error) {
- return null;
- }
- };
- const fnPicFileUploadOpen = () => {
- let fileUpload = document.getElementById("fileupload_pic");
- if (fileUpload != null) {
- fileUpload.click();
- }
- };
- const fnUploadPicFileCheck = () => {
- if (form.value.formValue5) {
- // 10Mb 이상은 업로드 불가
- if (form.value.formValue5.size > 10 * 1024 * 1024) {
- fnOpenCommPop("10mb 이상은 업로드가 불가합니다.");
- form.value.formValue5 = null;
- return;
- }
- // 이미지 파일 형식 체크
- let extension = form.value.formValue5.name.split(".").pop().toLowerCase();
- if (
- extension != "jpg" &&
- extension != "jpeg" &&
- extension != "png" &&
- extension != "gif"
- ) {
- fnOpenCommPop("파일 형식 또는 확장자가 올바르지 않습니다.");
- form.value.formValue5 = null;
- return;
- }
- objProc.validErrorMessage = "";
- // 이미지 미리보기
- let previewImage = new Image();
- let tempImageUrl = window.URL.createObjectURL(form.value.formValue5);
- //console.log(tempImageUrl);
- previewImage.src = tempImageUrl;
- items.value[0] = tempImageUrl;
- imgTemp.value = tempImageUrl;
- }
- };
- const fnDownloadFile = () => {
- window.location.href = `https://shopdeli.mycafe24.com/item/download/${zipInfo.value.file_path}`;
- }
- // 인플루언서 목록 조회
- const getInfluencerList = async () => {
- try {
- const params = {
- page: 1,
- size: 100, // 충분한 수량
- };
- const response = await useAxios().post('/user/list', params);
-
- if (response.data) {
- influencerList.value = response.data;
- } else {
- //console.error('❌ API 실패:', response.data.message);
- influencerList.value = [];
- }
- } catch (error) {
- //console.error('❌ 인플루언서 목록 조회 실패:', error);
- influencerList.value = [];
- }
- };
- // 인플루언서 검색 기능
- const searchInfluencers = () => {
- if (!influencerSearchQuery.value.trim()) {
- filteredInfluencerList.value = influencerList.value;
- return;
- }
-
- isSearching.value = true;
-
- setTimeout(() => {
- const query = influencerSearchQuery.value.toLowerCase().trim();
- filteredInfluencerList.value = influencerList.value.filter(influencer => {
- const nickname = (influencer.INFLUENCER_NICKNAME || '').toLowerCase();
- const name = (influencer.INFLUENCER_NAME || '').toLowerCase();
- return nickname.includes(query) || name.includes(query);
- });
- isSearching.value = false;
- }, 300);
- };
- // 소셜 아이콘 반환
- const getSocialIcon = (snsType) => {
- switch(snsType?.toLowerCase()) {
- case 'instagram': return 'mdi-instagram';
- case 'youtube': return 'mdi-youtube';
- case 'tiktok': return 'mdi-music-note';
- case 'twitter': return 'mdi-twitter';
- default: return 'mdi-web';
- }
- };
- // 인플루언서 선택 모달 열기
- const openInfluencerModal = async () => {
- await getInfluencerList();
- filteredInfluencerList.value = influencerList.value;
- selectedInfluencer.value = null;
- influencerSearchQuery.value = "";
- influencerModal.value = true;
- };
- // 인플루언서 선택 모달 닫기
- const closeInfluencerModal = () => {
- influencerModal.value = false;
- selectedInfluencer.value = null;
- influencerSearchQuery.value = "";
- };
- // 인플루언서 선택
- const selectInfluencer = (influencer) => {
- // 화면에 표시할 이름
- const displayName = influencer.NICK_NAME
- ? `${influencer.NICK_NAME} (${influencer.NAME})`
- : influencer.NAME;
-
- // 실제 전송할 SEQ 값과 표시용 이름 한번에 저장
- form.value.contact_inf = influencer.SEQ; // SEQ 값 저장
- form.value.contact_inf_display = displayName; // 유저이름 저장
-
- influencerModal.value = false;
- };
- // 브랜드사 목록 조회
- const getBrandList = async () => {
- try {
- const params = {
- page: 1,
- size: 100,
- };
- //console.log('🔍 getBrandList 호출됨:', params);
- const response = await useAxios().post('/user/brandlist', params);
-
- if (response.data && response.data.length > 0) {
- //console.log('📋 받아온 브랜드사 목록:', response.data.length, response.data);
- brandList.value = response.data;
- } else {
- //console.error('❌ 브랜드사 목록이 비어있음');
- brandList.value = [];
- }
- } catch (error) {
- //console.error('❌ 브랜드사 목록 조회 실패:', error);
- brandList.value = [];
- }
- };
- // 브랜드사 검색 기능
- const searchBrands = () => {
- if (!brandSearchQuery.value.trim()) {
- filteredBrandList.value = brandList.value;
- return;
- }
-
- isBrandSearching.value = true;
-
- setTimeout(() => {
- const query = brandSearchQuery.value.toLowerCase().trim();
- filteredBrandList.value = brandList.value.filter(brand => {
- const companyName = (brand.COMPANY_NAME || '').toLowerCase();
- const name = (brand.NAME || '').toLowerCase();
- return companyName.includes(query) || name.includes(query);
- });
- isBrandSearching.value = false;
- }, 300);
- };
- // 브랜드사 선택 모달 열기
- const openBrandModal = async () => {
- await getBrandList();
- filteredBrandList.value = brandList.value;
- selectedBrand.value = null;
- brandSearchQuery.value = "";
- brandModal.value = true;
- };
- // 브랜드사 선택 모달 닫기
- const closeBrandModal = () => {
- brandModal.value = false;
- selectedBrand.value = null;
- brandSearchQuery.value = "";
- };
- // 브랜드사 선택
- const selectBrand = (brand) => {
- // 화면에 표시할 이름
- const displayName = brand.COMPANY_NAME;
-
- // 실제 전송할 SEQ 값과 표시용 이름 한번에 저장
- form.value.contact_brd = brand.SEQ; // SEQ 값 저장
- form.value.contact_brd_display = displayName; // 회사명 저장
-
- brandModal.value = false;
- selectedBrand.value = brand;
- };
- // 브랜드사 이름 가져오기
- const getBrandNameBySeq = async (brandSeq) => {
- try {
- if (!brandSeq) return '';
-
- // 이미 로드된 목록에서 찾기
- if (brandList.value.length > 0) {
- const brand = brandList.value.find(b => b.SEQ == brandSeq);
- if (brand) {
- return brand.COMPANY_NAME;
- }
- }
-
- // 목록에 없으면 API 호출해서 전체 목록 가져오기
- await getBrandList();
- const brand = brandList.value.find(b => b.SEQ == brandSeq);
- if (brand) {
- return brand.COMPANY_NAME;
- }
-
- return `브랜드사 ID: ${brandSeq}`;
- } catch (error) {
- //console.error('브랜드사 이름 조회 실패:', error);
- return `브랜드사 ID: ${brandSeq}`;
- }
- };
- /*======================================================================
- | 작성 시퀀스
- | 1. 작성 컨펌
- | 2. 버튼 체크
- | 3. 등록시 -> 등록 API 호출
- ======================================================================*/
- const fnBtnEvt = () => {
- //await editorContent();
- nextTick(() => {
- if (addForm.value && typeof addForm.value.validate === "function") {
- addForm.value
- .validate()
- .then((isValid) => {
- if (
- isValid.valid
- ) {
- if (pageType.value == "I") fnRegEvt();
- else fnUpdEvt();
- } else {
- let param = {
- id: pageId,
- title: pageId,
- content: "필수항목을 입력해주세요.",
- yes: {
- text: "확인",
- isProc: false,
- }
- };
- $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
- }
- })
- .catch((err) => {
- });
- } else {
- }
- });
- };
- const fnOpenCommPop = (__TEXT) => {
- let param = {
- id: pageId,
- title: "알림",
- content: __TEXT,
- yes: {
- text: "확인",
- isProc: false,
- },
- };
- $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
- };
- const fnRegEvt = () => {
- let param = {
- id: pageId,
- title: pageId,
- content: "등록하시겠습니까?",
- yes: {
- text: "등록",
- isProc: true,
- event: "FN_INSERT",
- param: "",
- },
- no: {
- text: "취소",
- isProc: false,
- },
- };
- $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
- };
- const fnUpdEvt = () => {
- let param = {
- id: pageId,
- title: pageId,
- content: "수정하시겠습니까?",
- yes: {
- text: "확인",
- isProc: true,
- event: "FN_UPDATE",
- param: "",
- },
- no: {
- text: "취소",
- isProc: false,
- },
- };
- $eventBus.emit("OPEN_CONFIRM_POP_UP", param);
- };
- const fnInsert = async () => {
- const formData = new FormData();
- formData.append('name', form.value.formValue1);
- formData.append('price1', form.value.formValue2);
- formData.append('price2', form.value.formValue3);
- formData.append('deli_fee', form.value.formValue4);
- formData.append('thumb_file', form.value.formValue5);
- formData.append('sub_title', form.value.formValue6);
- formData.append('detail', updatedContent.value);
- formData.append('zip_file', form.value.formValue7);
- formData.append('status', form.value.formValue8);
- formData.append('show_yn', form.value.formValue9);
- formData.append('add_info', form.value.formValue10);
- formData.append('order_link', form.value.order_link);
- formData.append('order_start_date', dayjs(form.value.order_start_date).format('YYYY-MM-DD'));
- formData.append('order_end_date', dayjs(form.value.order_end_date).format('YYYY-MM-DD'));
- formData.append('item_type', itemType);
- formData.append('contact_inf', form.value.contact_inf);
- formData.append('contact_brd', form.value.contact_brd);
- // 벤더사의 COMPANY_NUMBER 사용
- const memberCompanyNumber = useAtStore.auth.companyNumber || "1";
- formData.append('company_number', memberCompanyNumber);
- useAxios()
- .post('/item/reg', formData, {
- headers: {'Content-Type': 'multipart/form-data'},
- })
- .then((res) => {
- router.push("/view/common/item");
- })
- .catch((error) => {
- })
- .finally(() => {
- });
- };
- const fnUpdate = async () => {
- let req = {
- seq: useDtStore.boardInfo.seq,
- };
- const formData = new FormData();
- formData.append('name', form.value.formValue1);
- // formData.append('price1', form.value.formValue2);
- // formData.append('price2', form.value.formValue3);
- // formData.append('deli_fee', form.value.formValue4);
- // if (form.value.formValue5 instanceof File) {
- // formData.append('thumb_file', form.value.formValue5);
- // }
- // formData.append('sub_title', form.value.formValue6);
- // formData.append('detail', updatedContent.value);
- // if (form.value.formValue7 instanceof File) {
- // formData.append('zip_file', form.value.formValue7);
- // }
- // formData.append('status', form.value.formValue8);
- // formData.append('show_yn', form.value.formValue9);
- // formData.append('add_info', form.value.formValue10);
- formData.append('order_link', form.value.order_link);
- formData.append('order_start_date', dayjs(form.value.order_start_date).format('YYYY-MM-DD'));
- formData.append('order_end_date', dayjs(form.value.order_end_date).format('YYYY-MM-DD'));
- formData.append('contact_inf', form.value.contact_inf);
- formData.append('contact_brd', form.value.contact_brd);
- // 벤더사의 COMPANY_NUMBER 사용
- const memberCompanyNumber = useAtStore.auth.companyNumber || "1";
- formData.append('company_number', memberCompanyNumber);
- try {
- const res = await useAxios().post(`/item/update/${req.seq}`, formData, {
- headers: { 'Content-Type': 'multipart/form-data' },
- });
- router.push("/view/common/item/detail");
- } catch (error) {
- }
- };
- const fnDetail = () => {
- let req = {
- seq: useDtStore.boardInfo.seq,
- };
- useAxios()
- .get(`/item/detail/${req.seq}`)
- .then(async (res) => {
- form.value.formValue1 = res.data.NAME;
- // form.value.formValue2 = res.data.PRICE1;
- // form.value.formValue3 = res.data.PRICE2;
- // form.value.formValue4 = res.data.DELI_FEE;
- // form.value.formValue5 = res.data.THUMB_FILE;
- // form.value.formValue6 = res.data.SUB_TITLE;
- // zipInfo.value.file_path = res.data.ZIP_FILE;
- // zipInfo.value.original_name = res.data.ZIP_FILE_ORIGIN;
- //에디터에 컨텐츠 전달
- //editorContentReq.value = res.data.DETAIL;
- form.value.formValue8 = res.data.STATUS;
- form.value.formValue9 = res.data.SHOW_YN;
- form.value.formValue10 = res.data.ADD_INFO;
- form.value.order_link = res.data.ORDER_LINK;
- form.value.order_start_date = res.data.ORDER_START_DATE;
- form.value.order_end_date = res.data.ORDER_END_DATE;
- form.value.contact_inf = res.data.CONTACT_INF;
- form.value.contact_brd = res.data.CONTACT_BRD;
-
- // contact_inf 값이 있으면 인플루언서 이름 조회
- if (res.data.CONTACT_INF) {
- const infData = await getInfName(res.data.CONTACT_INF);
- if (infData) {
- form.value.contact_inf_display = infData.NICK_NAME
- ? `${infData.NICK_NAME} (${infData.NAME})`
- : infData.NAME;
- }
- }
- if (res.data.CONTACT_BRD) {
- const brdData = await getBrdName(res.data.CONTACT_BRD);
- if (brdData) {
- form.value.contact_brd_display = brdData.NAME;
- }
- }
- //썸네일 파일이 있으면 넣어줌
- if(form.value.formValue5){
- imgTemp.value = `https://shopdeli.mycafe24.com/writable/uploads/item/thumb/${form.value.formValue5}`;
- }
- })
- .catch((error) => {
- })
- .finally(() => {
- });
- };
- /*=======================================================================
- | 최종 에디터 이미지 url치환 : S
- /*=======================================================================*/
- // const editorContent = async () => {
- // const content = sunEditorWrapper.value.getEditorContent();
- // updatedContent.value = await processEditorContent(content);
- // console.log("Updated content:", updatedContent.value);
- // };
- // Base64 데이터를 Blob으로 변환
- const base64ToBlob = (base64, mimeType) => {
- const byteString = atob(base64.split(",")[1]);
- const arrayBuffer = new ArrayBuffer(byteString.length);
- const uint8Array = new Uint8Array(arrayBuffer);
- for (let i = 0; i < byteString.length; i++) {
- uint8Array[i] = byteString.charCodeAt(i);
- }
- return new Blob([uint8Array], { type: mimeType });
- };
- // Base64 데이터를 File 객체로 변환
- const base64ToFile = (base64, mimeType, fileName) => {
- const blob = base64ToBlob(base64, mimeType);
- return new File([blob], fileName, { type: mimeType });
- };
- // 이미지 업로드 처리 (useAxios)
- const uploadImage = async (file) => {
- const formDataEdt = new FormData();
- formDataEdt.append("picObj", file);
- return useAxios()
- .post("/pic/upload", formDataEdt, {
- headers: { "Content-Type": "multipart/form-data" },
- })
- .then((res) => {
- const filePath = res.data.ogn_name.path.replace(/.*\/files\//, "");
- const fileName = res.data.ogn_name.file_name;
- return `${apiUrl.value}/images/${filePath}/${fileName}`; // 최종 URL 반환
- })
- .catch((error) => {
- console.error("Image upload failed:", error);
- return null;
- });
- };
- // 에디터 내용 처리 및 이미지 업로드
- const processEditorContent = async (content) => {
- const parser = new DOMParser();
- const doc = parser.parseFromString(content, "text/html");
- const images = doc.querySelectorAll("img");
- for (let i = 0; i < images.length; i++) {
- const img = images[i];
- const src = img.src;
- if (src.startsWith("data:image")) {
- // MIME 타입과 파일 이름 추출
- const mimeType = src.split(";")[0].split(":")[1];
- const extension = mimeType.split("/")[1];
- const fileName = `image-${i + 1}.${extension}`;
- // Base64 데이터를 File 객체로 변환
- const file = base64ToFile(src, mimeType, fileName);
- // 이미지 업로드 및 URL 반환
- const finalUrl = await uploadImage(file);
- if (finalUrl) {
- img.src = finalUrl; // 이미지 src 업데이트
- }
- }
- }
- return doc.body.innerHTML; // 최종 수정된 HTML 반환
- };
- /*=======================================================================
- | 최종 에디터 이미지 url치환 : E
- /*=======================================================================*/
- /************************************************************************
- | 팝업 이벤트버스 정의
- ************************************************************************/
- $eventBus.off("FN_INSERT");
- $eventBus.on("FN_INSERT", () => {
- fnInsert();
- });
- $eventBus.off("FN_UPDATE");
- $eventBus.on("FN_UPDATE", () => {
- fnUpdate();
- });
- $eventBus.off("FN_DELETE");
- $eventBus.on("FN_DELETE", () => {
- fnDelete();
- });
- /************************************************************************
- | 라이프사이클
- ************************************************************************/
- onMounted(() => {
- pageType.value = useDtStore.boardInfo.pageType;
-
- if(pageType.value == "I"){
- if(itemType == "G"){
- pageId.value = "공동구매 등록"
- } else {
- pageId.value = "제품 등록"
- }
- } else if(pageType.value == "U"){
- if(itemType == "G"){
- pageId.value = "공동구매 수정"
- } else {
- pageId.value = "제품 수정"
- }
- } else {
- if(itemType == "G"){
- pageId.value = "공동구매 상세"
- } else {
- pageId.value = "제품 상세"
- }
- }
- //상세 등록 아니 리스트 클릭시 상세 정보로 접근
- if (pageType.value !== "I") {
- fnDetail();
- }
- });
- /************************************************************************
- | WATCH
- ************************************************************************/
- // 시작일이 변경될 때, 종료일이 시작일보다 이전이면 종료일을 시작일과 같게 설정
- watch(() => form.value.order_start_date, (newStartDate) => {
- if (newStartDate && form.value.order_end_date && form.value.order_end_date < newStartDate) {
- form.value.order_end_date = newStartDate;
- }
- });
- // 종료일이 변경될 때, 종료일이 시작일보다 이전이면 시작일과 같게 설정
- watch(() => form.value.order_end_date, (newEndDate) => {
- if (newEndDate && form.value.order_start_date && newEndDate < form.value.order_start_date) {
- form.value.order_end_date = form.value.order_start_date;
- }
- });
- </script>
- <style scoped>
- .cursor-pointer {
- cursor: pointer;
- }
- .cursor-pointer:hover {
- background-color: #f5f5f5;
- }
- .order-link {
- color: #1976d2;
- text-decoration: none;
- display: inline-flex;
- align-items: center;
- transition: color 0.2s;
- }
- .order-link:hover {
- color: #1565c0;
- text-decoration: underline;
- }
- .no-link {
- color: #999;
- font-style: italic;
- }
- </style>
|