|
@@ -144,6 +144,7 @@
|
|
|
});
|
|
});
|
|
|
const imgTemp = ref("");
|
|
const imgTemp = ref("");
|
|
|
const tblItems = ref([]); // stat 데이터
|
|
const tblItems = ref([]); // stat 데이터
|
|
|
|
|
+ const isExcelProcessed = ref(false); // 엑셀 업로드로 데이터가 처리되었는지 여부
|
|
|
const form = ref({
|
|
const form = ref({
|
|
|
formValue1: "",
|
|
formValue1: "",
|
|
|
formValue2: "",
|
|
formValue2: "",
|
|
@@ -330,6 +331,7 @@
|
|
|
// 맨 앞에 추가 (unshift 사용)
|
|
// 맨 앞에 추가 (unshift 사용)
|
|
|
tblItems.value.unshift(newRow);
|
|
tblItems.value.unshift(newRow);
|
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
|
|
|
+ isExcelProcessed.value = false; // 수동 추가이므로 엑셀 처리 아님
|
|
|
|
|
|
|
|
// ag-grid 데이터 갱신
|
|
// ag-grid 데이터 갱신
|
|
|
if (gridApi.value) {
|
|
if (gridApi.value) {
|
|
@@ -502,13 +504,14 @@
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 기존 주문 데이터와 매칭 검증
|
|
|
|
|
- const matchResults = await validateAndMatchOrders(mappedData);
|
|
|
|
|
- const { matchedData, unmatchedData, errors } = matchResults;
|
|
|
|
|
|
|
+ // 기존 데이터와 엑셀 데이터를 주문일 기준으로 분리 처리
|
|
|
|
|
+ const processResult = await processInfluencerExcelData(mappedData);
|
|
|
|
|
+ const { updatedData, newData, errors } = processResult;
|
|
|
|
|
|
|
|
- // 매칭된 데이터만 그리드에 추가
|
|
|
|
|
- tblItems.value = [...matchedData];
|
|
|
|
|
|
|
+ // 업데이트된 기존 데이터 + 신규 데이터를 합쳐서 그리드에 설정
|
|
|
|
|
+ tblItems.value = [...updatedData, ...newData];
|
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
|
|
|
+ isExcelProcessed.value = true; // 엑셀 처리 완료 표시
|
|
|
|
|
|
|
|
// ag-grid 데이터 갱신
|
|
// ag-grid 데이터 갱신
|
|
|
if (gridApi.value) {
|
|
if (gridApi.value) {
|
|
@@ -516,20 +519,29 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 결과에 따른 사용자 피드백
|
|
// 결과에 따른 사용자 피드백
|
|
|
- if (matchedData.length > 0 && unmatchedData.length === 0) {
|
|
|
|
|
- $toast.success(
|
|
|
|
|
- `${matchedData.length}건의 데이터가 성공적으로 업로드되었습니다.`
|
|
|
|
|
- );
|
|
|
|
|
- } else if (matchedData.length > 0 && unmatchedData.length > 0) {
|
|
|
|
|
- $toast.warning(
|
|
|
|
|
- `${matchedData.length}건 업로드 완료, ${unmatchedData.length}건 매칭 실패`
|
|
|
|
|
- );
|
|
|
|
|
- showUnmatchedDataModal(unmatchedData, errors);
|
|
|
|
|
|
|
+ const totalProcessed = updatedData.length + newData.length;
|
|
|
|
|
+ if (totalProcessed > 0) {
|
|
|
|
|
+ if (updatedData.length > 0 && newData.length > 0) {
|
|
|
|
|
+ $toast.success(
|
|
|
|
|
+ `총 ${totalProcessed}건 처리완료 (업데이트: ${updatedData.length}건, 신규: ${newData.length}건)`
|
|
|
|
|
+ );
|
|
|
|
|
+ } else if (updatedData.length > 0) {
|
|
|
|
|
+ $toast.success(
|
|
|
|
|
+ `${updatedData.length}건의 기존 주문이 업데이트되었습니다.`
|
|
|
|
|
+ );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $toast.success(
|
|
|
|
|
+ `${newData.length}건의 신규 주문이 추가되었습니다.`
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
$toast.error(
|
|
$toast.error(
|
|
|
- "매칭된 주문 데이터가 없습니다. 구매자명과 연락처를 확인해주세요."
|
|
|
|
|
|
|
+ "처리된 데이터가 없습니다. 엑셀 데이터를 확인해주세요."
|
|
|
);
|
|
);
|
|
|
- showUnmatchedDataModal(unmatchedData, errors);
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (errors.length > 0) {
|
|
|
|
|
+ console.warn("처리 중 발생한 오류들:", errors);
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error("엑셀 파일 처리 중 오류:", error);
|
|
console.error("엑셀 파일 처리 중 오류:", error);
|
|
@@ -721,6 +733,83 @@
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const processInfluencerExcelData = async (uploadData) => {
|
|
|
|
|
+ const updatedData = [];
|
|
|
|
|
+ const newData = [];
|
|
|
|
|
+ const errors = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 기존 데이터를 맵으로 변환 (구매자명 + 연락처 키로 매핑)
|
|
|
|
|
+ const existingDataMap = new Map();
|
|
|
|
|
+ tblItems.value.forEach(item => {
|
|
|
|
|
+ const key = `${item.BUYER_NAME?.trim()}_${item.PHONE?.replace(/\s/g, '').replace(/-/g, '')}`;
|
|
|
|
|
+ existingDataMap.set(key, item);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ uploadData.forEach((excelItem, index) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 엑셀 데이터의 키 생성
|
|
|
|
|
+ const key = `${excelItem.BUYER_NAME?.trim()}_${excelItem.PHONE?.replace(/\s/g, '').replace(/-/g, '')}`;
|
|
|
|
|
+ const existingItem = existingDataMap.get(key);
|
|
|
|
|
+
|
|
|
|
|
+ if (existingItem) {
|
|
|
|
|
+ // 기존 데이터가 있는 경우 주문일 비교
|
|
|
|
|
+ const existingOrderDate = formatDateForComparison(existingItem.ORDER_DATE);
|
|
|
|
|
+ const excelOrderDate = formatDateForComparison(excelItem.ORDER_DATE);
|
|
|
|
|
+
|
|
|
|
|
+ if (existingOrderDate === excelOrderDate) {
|
|
|
|
|
+ // 주문일이 같으면 업데이트 (배송정보 등 다른 필드 업데이트)
|
|
|
|
|
+ updatedData.push({
|
|
|
|
|
+ ...existingItem,
|
|
|
|
|
+ ADDRESS: excelItem.ADDRESS || existingItem.ADDRESS,
|
|
|
|
|
+ EMAIL: excelItem.EMAIL || existingItem.EMAIL,
|
|
|
|
|
+ QTY: excelItem.QTY || existingItem.QTY,
|
|
|
|
|
+ TOTAL: excelItem.TOTAL || existingItem.TOTAL,
|
|
|
|
|
+ DELI_COMP: excelItem.DELI_COMP || existingItem.DELI_COMP,
|
|
|
|
|
+ DELI_NUMB: excelItem.DELI_NUMB || existingItem.DELI_NUMB
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 주문일이 다르면 신규 데이터로 추가
|
|
|
|
|
+ newData.push({
|
|
|
|
|
+ ...excelItem,
|
|
|
|
|
+ // 인플루언서 정보는 기존 데이터에서 가져오기
|
|
|
|
|
+ NICK_NAME: existingItem.NICK_NAME || "알 수 없음"
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 기존 데이터가 없으면 신규 데이터로 추가
|
|
|
|
|
+ newData.push({
|
|
|
|
|
+ ...excelItem,
|
|
|
|
|
+ NICK_NAME: "알 수 없음" // 기본값
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ errors.push(`${index + 1}행 처리 중 오류: ${error.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 업데이트되지 않은 기존 데이터도 유지
|
|
|
|
|
+ tblItems.value.forEach(existingItem => {
|
|
|
|
|
+ const key = `${existingItem.BUYER_NAME?.trim()}_${existingItem.PHONE?.replace(/\s/g, '').replace(/-/g, '')}`;
|
|
|
|
|
+ const hasUpdate = uploadData.some(excelItem => {
|
|
|
|
|
+ const excelKey = `${excelItem.BUYER_NAME?.trim()}_${excelItem.PHONE?.replace(/\s/g, '').replace(/-/g, '')}`;
|
|
|
|
|
+ return excelKey === key;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 업데이트되지 않은 기존 데이터는 updatedData에 그대로 추가
|
|
|
|
|
+ if (!hasUpdate) {
|
|
|
|
|
+ updatedData.push(existingItem);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return { updatedData, newData, errors };
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const formatDateForComparison = (dateStr) => {
|
|
|
|
|
+ if (!dateStr) return '';
|
|
|
|
|
+ // YYYY.MM.DD 또는 YYYY-MM-DD 형태를 YYYY-MM-DD로 통일
|
|
|
|
|
+ return dateStr.toString().replace(/\./g, '-').trim();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const validateAndMatchOrders = async (uploadData) => {
|
|
const validateAndMatchOrders = async (uploadData) => {
|
|
|
try {
|
|
try {
|
|
|
const response = await useAxios().post("/deli/validateOrders", {
|
|
const response = await useAxios().post("/deli/validateOrders", {
|
|
@@ -872,6 +961,7 @@
|
|
|
console.log(res.data);
|
|
console.log(res.data);
|
|
|
tblItems.value = res.data;
|
|
tblItems.value = res.data;
|
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
pageObj.value.totalCnt = tblItems.value.length;
|
|
|
|
|
+ isExcelProcessed.value = false; // 초기 로드이므로 엑셀 처리 아님
|
|
|
})
|
|
})
|
|
|
.catch((error) => {
|
|
.catch((error) => {
|
|
|
$toast.error("제품 정보를 불러오는 중 오류가 발생했습니다.");
|
|
$toast.error("제품 정보를 불러오는 중 오류가 발생했습니다.");
|
|
@@ -890,6 +980,7 @@
|
|
|
const deliveryData = {
|
|
const deliveryData = {
|
|
|
item_seq: useDtStore.boardInfo.seq,
|
|
item_seq: useDtStore.boardInfo.seq,
|
|
|
inf_seq: memberSeq,
|
|
inf_seq: memberSeq,
|
|
|
|
|
+ useOrderDateMatching: isExcelProcessed.value, // 엑셀 업로드로 처리된 경우 true
|
|
|
deliveryList: tblItems.value.map((item) => ({
|
|
deliveryList: tblItems.value.map((item) => ({
|
|
|
buyerName: item.BUYER_NAME,
|
|
buyerName: item.BUYER_NAME,
|
|
|
address: item.ADDRESS,
|
|
address: item.ADDRESS,
|