shipping.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <template>
  2. <div>
  3. <div class="inner--headers">
  4. <h2>{{ pageId }}</h2>
  5. <div class="bread--crumbs--wrap">
  6. <span>홈</span>
  7. <span>{{ pageId }}</span>
  8. </div>
  9. </div>
  10. <div class="search--modules type2">
  11. <div class="search--inner">
  12. <div class="form--cont--filter">
  13. <v-select
  14. v-model="filter"
  15. :items="filderArr"
  16. variant="outlined"
  17. class="custom-select"
  18. >
  19. </v-select>
  20. </div>
  21. <div class="form--cont--text">
  22. <v-text-field
  23. v-model="searchModel"
  24. class="custom-input mini"
  25. style="width: 100%"
  26. placeholder="검색어를 입력하세요"
  27. ></v-text-field>
  28. </div>
  29. </div>
  30. <div class="search--inner">
  31. <div class="calendar-wrap ml--0">
  32. <div class="calendar">
  33. <VueDatePicker
  34. :format="datePickerFormat"
  35. v-model="searchStartDate"
  36. placeholder="날짜를 선택하세요"
  37. :auto-apply="true"
  38. week-start="0"
  39. ></VueDatePicker>
  40. </div>
  41. <span class="text">~</span>
  42. <div class="calendar">
  43. <VueDatePicker
  44. v-model="searchEndDate"
  45. :format="datePickerFormat"
  46. placeholder="날짜를 선택하세요"
  47. :auto-apply="true"
  48. week-start="0"
  49. ></VueDatePicker>
  50. </div>
  51. </div>
  52. </div>
  53. <v-btn
  54. class="custom-btn btn-blue mini sch--btn"
  55. @click="fnSearch(searchModel, filter)"
  56. >검색</v-btn
  57. >
  58. </div>
  59. <div class="data--list--wrap">
  60. <div class="btn--actions--wrap">
  61. <div class="left--sections">
  62. <v-btn
  63. v-if="memberType === 'VENDOR'"
  64. class="custom-btn btn-blue mini"
  65. @click="markAsDelivered"
  66. :disabled="selectedItems.length === 0"
  67. >
  68. <i class="ico"></i>배송완료 처리
  69. </v-btn>
  70. </div>
  71. <div class="right--sections">
  72. <span class="total-count">총 {{ tblItems.length }}건</span>
  73. </div>
  74. </div>
  75. <div class="tbl-wrapper">
  76. <div class="tbl-wrap">
  77. <!-- ag grid -->
  78. <ag-grid-vue
  79. style="width: 100%; height: calc(10 * 2.94rem)"
  80. class="ag-theme-quartz"
  81. :gridOptions="gridOptions"
  82. :rowData="tblItems"
  83. :rowSelection="memberType === 'VENDOR' ? 'multiple' : 'single'"
  84. :paginationPageSize="pageObj.pageSize"
  85. :suppressPaginationPanel="true"
  86. @grid-ready="onGridReady"
  87. @selection-changed="onSelectionChanged"
  88. >
  89. </ag-grid-vue>
  90. <!-- 페이징 -->
  91. <div class="ag-grid-custom-pagenations">
  92. <pagination @chg_page="chgPage" :pageObj="pageObj"></pagination>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. </template>
  99. <script setup>
  100. import VueDatePicker from "@vuepic/vue-datepicker";
  101. import "@vuepic/vue-datepicker/dist/main.css";
  102. import { AgGridVue } from "ag-grid-vue3";
  103. import dayjs from 'dayjs';
  104. import pagination from "../components/common/pagination.vue";
  105. definePageMeta({
  106. layout: "default",
  107. });
  108. const props = defineProps({
  109. propsData: {
  110. type: Object,
  111. default: () => {},
  112. },
  113. });
  114. const useDtStore = useDetailStore();
  115. const useAtStore = useAuthStore();
  116. const memberType = useAtStore.auth.memberType;
  117. const memberSeq = useAtStore.auth.seq;
  118. const searchModel = ref("");
  119. const searchStartDate = ref("");
  120. const searchEndDate = ref("");
  121. const datePickerFormat = "yyyy-MM-dd";
  122. const filter = ref("");
  123. const filderArr = ref([
  124. { title: "전체", value: "" },
  125. { title: "제품명", value: "name" },
  126. { title: "구매자명", value: "buyer" },
  127. ]);
  128. const selectedItems = ref([]);
  129. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
  130. const router = useRouter();
  131. const pageId = ref("배송중 관리");
  132. let pageObj = ref({
  133. page: 1,
  134. pageMaxNumSize: 10,
  135. pageSize: 10,
  136. totalCnt: 0,
  137. });
  138. const tblItems = ref([]);
  139. const remToPx = () => parseFloat(getComputedStyle(document.documentElement).fontSize);
  140. const rowHeightRem = 2.65;
  141. const rowHeightPx = rowHeightRem * remToPx();
  142. const gridApi = shallowRef();
  143. // gridOption
  144. const gridOptions = {
  145. columnDefs: [
  146. ...(memberType === 'VENDOR' ? [{ checkboxSelection: true, headerCheckboxSelection: true, width: 50 }] : []),
  147. {
  148. headerName: "No",
  149. valueGetter: (params) => params.api.getDisplayedRowCount() - params.node.rowIndex,
  150. sortable: false,
  151. width: 70,
  152. },
  153. {
  154. headerName: "제품명",
  155. field: "ITEM_NAME",
  156. width: 200,
  157. },
  158. {
  159. headerName: "구매자명",
  160. field: "BUYER_NAME",
  161. width: 120,
  162. },
  163. {
  164. headerName: "연락처",
  165. field: "PHONE",
  166. width: 140,
  167. },
  168. {
  169. headerName: "주소",
  170. field: "ADDRESS",
  171. width: 250,
  172. },
  173. {
  174. headerName: "수량",
  175. field: "QTY",
  176. width: 80,
  177. cellRenderer: (params) => {
  178. return Number(params.value).toLocaleString();
  179. },
  180. },
  181. {
  182. headerName: "금액",
  183. field: "TOTAL",
  184. width: 120,
  185. cellRenderer: (params) => {
  186. return Number(params.value).toLocaleString() + '원';
  187. },
  188. },
  189. {
  190. headerName: "배송업체",
  191. field: "DELI_COMP",
  192. width: 120,
  193. },
  194. {
  195. headerName: "송장번호",
  196. field: "DELI_NUMB",
  197. width: 150,
  198. },
  199. {
  200. headerName: "배송시작일",
  201. field: "SHIPPING_DATE",
  202. width: 140,
  203. cellRenderer: (params) => {
  204. return params.value ? dayjs(params.value).format('YYYY-MM-DD') : '';
  205. },
  206. },
  207. ],
  208. rowData: tblItems.value,
  209. autoSizeStrategy: {
  210. type: "fitGridWidth",
  211. },
  212. suppressMovableColumns: true,
  213. headerHeight: rowHeightPx,
  214. rowHeight: rowHeightPx,
  215. pagination: true,
  216. suppressPaginationPanel: true,
  217. ...(memberType === 'VENDOR' ? {
  218. rowSelection: {
  219. checkboxes: true,
  220. headerCheckbox: true,
  221. enableClickSelection: false,
  222. mode: "multiRow",
  223. }
  224. } : {}),
  225. };
  226. const onGridReady = (__PARAMS) => {
  227. gridApi.value = __PARAMS.api;
  228. };
  229. const onSelectionChanged = () => {
  230. if (memberType === 'VENDOR') {
  231. selectedItems.value = gridApi.value.getSelectedRows();
  232. }
  233. };
  234. const chgPage = (__PAGE) => {
  235. pageObj.value.page = __PAGE;
  236. gridApi.value.paginationGoToPage(__PAGE - 1);
  237. };
  238. const getShippingList = async () => {
  239. let _req = {
  240. MEMBER_TYPE: memberType
  241. };
  242. if (memberType === "INFLUENCER") {
  243. _req.INF_SEQ = memberSeq;
  244. } else if (memberType === "VENDOR") {
  245. _req.COMPANY_NUMBER = useAtStore.auth.companyNumber || "1";
  246. }
  247. console.log('배송중 리스트 API 요청:', _req);
  248. await useAxios()
  249. .post("/deli/shipping", _req)
  250. .then((res) => {
  251. console.log('배송중 리스트 API 응답:', res.data);
  252. console.log('응답 데이터 개수:', res.data?.length || 0);
  253. tblItems.value = res.data;
  254. pageObj.value.totalCnt = res.data.length;
  255. // ag-grid 데이터 갱신
  256. if (gridApi.value) {
  257. gridApi.value.setGridOption('rowData', tblItems.value);
  258. }
  259. })
  260. .catch((error) => {
  261. console.error('배송중 리스트 API 오류:', error);
  262. $toast.error('배송중 리스트를 불러오는 중 오류가 발생했습니다.');
  263. });
  264. };
  265. const fnSearch = (__KEYWORD, __FILTER) => {
  266. // 검색 로직 구현
  267. let filteredData = tblItems.value;
  268. if (__KEYWORD && __KEYWORD.trim() !== '') {
  269. filteredData = tblItems.value.filter(item => {
  270. if (__FILTER === 'name') {
  271. return item.ITEM_NAME && item.ITEM_NAME.toLowerCase().includes(__KEYWORD.toLowerCase());
  272. } else if (__FILTER === 'buyer') {
  273. return item.BUYER_NAME && item.BUYER_NAME.toLowerCase().includes(__KEYWORD.toLowerCase());
  274. } else {
  275. // 전체 검색
  276. return (item.ITEM_NAME && item.ITEM_NAME.toLowerCase().includes(__KEYWORD.toLowerCase())) ||
  277. (item.BUYER_NAME && item.BUYER_NAME.toLowerCase().includes(__KEYWORD.toLowerCase()));
  278. }
  279. });
  280. }
  281. if (gridApi.value) {
  282. gridApi.value.setGridOption('rowData', filteredData);
  283. }
  284. };
  285. const markAsDelivered = async () => {
  286. if (selectedItems.value.length === 0) {
  287. $toast.error('배송완료 처리할 항목을 선택해주세요.');
  288. return;
  289. }
  290. const orderIds = selectedItems.value.map(item => item.SEQ);
  291. await useAxios()
  292. .post("/deli/markDelivered", { order_ids: orderIds })
  293. .then((res) => {
  294. $toast.success('배송완료 처리되었습니다.');
  295. getShippingList(); // 리스트 새로고침
  296. selectedItems.value = []; // 선택 초기화
  297. })
  298. .catch((error) => {
  299. $toast.error('배송완료 처리 중 오류가 발생했습니다.');
  300. });
  301. };
  302. onMounted(() => {
  303. getShippingList();
  304. // 날짜 초기화
  305. const today = dayjs();
  306. searchStartDate.value = today.subtract(30, 'day').format('YYYY-MM-DD');
  307. searchEndDate.value = today.format('YYYY-MM-DD');
  308. });
  309. </script>
  310. <style scoped>
  311. .total-count {
  312. font-size: 14px;
  313. color: #666;
  314. margin-left: 10px;
  315. }
  316. .btn--actions--wrap .right--sections {
  317. display: flex;
  318. align-items: center;
  319. }
  320. </style>