index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <script setup lang="tsx">
  2. import { reactive, ref, unref, useTemplateRef, watch } from 'vue';
  3. import { NImage, NTag } from 'naive-ui';
  4. import { fetchGetDeliveryOrderList, fetchGetDeliveryStatusNum } from '@/service/api/delivery/normal-orde';
  5. import { useAppStore } from '@/store/modules/app';
  6. import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
  7. import { copyTextToClipboard } from '@/utils/zt';
  8. import { $t } from '@/locales';
  9. import { useForm } from '@/components/zt/Form/hooks/useForm';
  10. import { SearchForm, orderStatus, orderStatusEnum, refundStatus } from './normal-order';
  11. import DeliveryModal from './component/delivery-modal.vue';
  12. import NormalMoadl from './component/normal-modal.vue';
  13. const appStore = useAppStore();
  14. const checkedRowKeys = ref([]);
  15. const activeTab = ref('all');
  16. const statusList = ref<{ label: string; value: string; num?: number }[]>([]);
  17. const orderMoadl = useTemplateRef('orderMoadl');
  18. const ShipmentModal = useTemplateRef('Shipment');
  19. const [registerSearchForm, { getFieldsValue }] = useForm({
  20. schemas: SearchForm,
  21. showAdvancedButton: false,
  22. labelWidth: 120,
  23. layout: 'horizontal',
  24. gridProps: {
  25. cols: '1 xl:4 s:1 l:3',
  26. itemResponsive: true
  27. },
  28. collapsedRows: 1
  29. });
  30. const searchForm = ref();
  31. const searchParams = reactive({
  32. current: 1,
  33. size: 10
  34. });
  35. const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedTable({
  36. api: () => fetchGetDeliveryOrderList({ ...searchParams, orderStatus: activeTab.value, ...unref(searchForm) }),
  37. transform: response => defaultTransform(response),
  38. paginationProps: {
  39. pageSizes: [10, 20, 50, 100, 150, 200]
  40. },
  41. onPaginationParamsChange: params => {
  42. searchParams.current = Number(params.page);
  43. searchParams.size = Number(params.pageSize);
  44. },
  45. columns: () => [
  46. {
  47. key: 'orderItems',
  48. title: '商品',
  49. align: 'left',
  50. width: 200,
  51. colSpan: (_rowData, _rowIndex) => 2,
  52. render: row => {
  53. return (
  54. <div>
  55. <div class={'mb3 flex items-center'}>
  56. <n-tag>
  57. <div class={'flex items-center'}>
  58. 订单编号:{row.orderNumber}
  59. <div onClick={() => handleCopy(row)}>
  60. <svgIcon icon={'bxs:copy'} class={'mx-3 cursor-pointer text-[#f97316]'}></svgIcon>
  61. </div>
  62. 下单时间:
  63. {row.createTime} 门店名称 {row.shopName}
  64. </div>
  65. </n-tag>
  66. </div>
  67. {row.orderItems?.map(item => {
  68. return (
  69. <div class={'mb-3 h-80px flex items-center'}>
  70. <NImage src={item.pic} class="h-[80px] min-w-80px w-[80px]" lazy />
  71. <div class={'ml12px flex-1'}>
  72. <div class={'w-full flex items-center justify-between'}>
  73. <div class={'w200px'}>
  74. <n-ellipsis class={'w200px'}>
  75. <span class={'w-full text-left text-15px font-semibold'}>{item.skuName}</span>
  76. </n-ellipsis>
  77. </div>
  78. <div class={'w150px pl30px text-left'}>
  79. <div class={'text-16px font-semibold'}>¥{item.price} </div>
  80. </div>
  81. </div>
  82. <div class={'w-full flex items-center justify-between'}>
  83. <div class={'w200px text-gray'}>规格: {item.spec}</div>
  84. <div class={'w150px pl30px text-left text-gray'}>x{item.prodCount} </div>
  85. </div>
  86. </div>
  87. </div>
  88. );
  89. })}
  90. </div>
  91. );
  92. }
  93. },
  94. {
  95. key: 'deptName',
  96. title: '单价(元)/数量',
  97. align: 'center',
  98. width: 100
  99. },
  100. {
  101. key: 'actualTotal',
  102. title: '实付金额(元)',
  103. align: 'center',
  104. width: 120,
  105. render: row => {
  106. return (
  107. <div class={'mt7'}>
  108. <div class={'text-16px text-#ff0000 font-semibold'}>{row.actualTotal} 元</div>
  109. <div>共 {row.goodsTotalCount} 件</div>
  110. </div>
  111. );
  112. }
  113. },
  114. {
  115. key: 'payType',
  116. title: '支付方式',
  117. align: 'center',
  118. width: 120,
  119. render: row => {
  120. return (
  121. <NTag class={'mt7'} type="success">
  122. {row.payType == 0 || row.payType == 1 ? '微信' : '未支付'}
  123. </NTag>
  124. );
  125. }
  126. },
  127. {
  128. key: 'seq',
  129. title: '买家/收货人',
  130. align: 'center',
  131. width: 120,
  132. render: row => {
  133. return (
  134. <div class={'mt7'}>
  135. <div>{row.receiver}</div>
  136. <div>{row.userMobile}</div>
  137. </div>
  138. );
  139. }
  140. },
  141. {
  142. key: 'status',
  143. title: '订单状态',
  144. align: 'center',
  145. width: 320,
  146. render: row => {
  147. const statusKey = row.hbOrderStatus as keyof typeof orderStatus;
  148. const statusText = orderStatus[statusKey] || '未知状态';
  149. return <NTag class={'mt7'}>{statusText}</NTag>;
  150. }
  151. },
  152. {
  153. key: 'createTime',
  154. title: '售后状态',
  155. align: 'center',
  156. width: 160,
  157. render: row => {
  158. const statusKey = row.refundStatus as keyof typeof refundStatus;
  159. const statusText = refundStatus[statusKey] || '暂无售后';
  160. return <NTag class={'mt7'}>{statusText}</NTag>;
  161. }
  162. },
  163. {
  164. key: 'operate',
  165. title: $t('common.operate'),
  166. align: 'center',
  167. width: 130,
  168. fixed: 'right',
  169. render: row => {
  170. return (
  171. <div class={'mt7'}>
  172. <n-button size="small" type="primary" quaternary onClick={() => handleOpenMoadl(row)}>
  173. 查看订单
  174. </n-button>
  175. {(row.hbOrderStatus == orderStatusEnum.WAIT_DELIVERY ||
  176. row.hbOrderStatus == orderStatusEnum.ORDER_WAIT_DELIVERY ||
  177. row.hbOrderStatus == orderStatusEnum.ORDER_DELIVERY ||
  178. row.hbOrderStatus == orderStatusEnum.ORDER_DELIVERY) && (
  179. <n-button size="small" type="primary" quaternary onClick={() => handleDeivery(row)}>
  180. {row.dvyFlowId ? '修改物流' : '发货'}
  181. </n-button>
  182. )}
  183. </div>
  184. );
  185. }
  186. }
  187. ]
  188. });
  189. async function handleDeivery(row: Api.delivery.deliveryOrder) {
  190. if (!row.orderNumber) {
  191. window.$message?.error('订单异常');
  192. return;
  193. }
  194. ShipmentModal.value?.handleOpenOrder(row.orderNumber);
  195. }
  196. function handleOpenMoadl(row: Api.delivery.deliveryOrder) {
  197. if (!row.orderNumber) {
  198. window.$message?.error('订单异常');
  199. return;
  200. }
  201. orderMoadl.value?.open(String(row.orderNumber));
  202. }
  203. async function getNums() {
  204. const { data: keyData } = await fetchGetDeliveryStatusNum();
  205. if (!keyData) return;
  206. const orderStatusList = [
  207. {
  208. label: '全部',
  209. value: 'all'
  210. },
  211. {
  212. label: '待支付',
  213. value: 'paddingPay'
  214. },
  215. {
  216. label: '待发货',
  217. value: 'paddingShipped'
  218. },
  219. {
  220. label: '待收货',
  221. value: 'paddingReceived'
  222. },
  223. {
  224. label: '已完成',
  225. value: 'completed'
  226. },
  227. {
  228. label: '已取消',
  229. value: 'cancel'
  230. }
  231. ];
  232. const updatedOrderStatusList = orderStatusList.map(item => {
  233. const key = item.value as keyof typeof keyData;
  234. if (Object.hasOwn(keyData, key)) {
  235. return {
  236. ...item,
  237. num: keyData[key]
  238. };
  239. }
  240. return item;
  241. });
  242. // console.log(updatedOrderStatusList, 'updatedOrderStatusList');
  243. statusList.value = updatedOrderStatusList;
  244. }
  245. getNums();
  246. watch(
  247. () => [activeTab.value],
  248. () => {
  249. searchParams.current = 1;
  250. getData();
  251. }
  252. );
  253. function handleSearch() {
  254. const form = getFieldsValue();
  255. if (form.createTime) {
  256. form.startTime = form.createTime[0];
  257. form.endTime = form.createTime[1];
  258. delete form.createTime;
  259. }
  260. searchForm.value = form;
  261. getData();
  262. }
  263. function handleReset() {
  264. searchForm.value = getFieldsValue();
  265. getData();
  266. }
  267. async function handleCopy(row: Api.delivery.deliveryOrder) {
  268. if (!row.orderNumber) {
  269. window.$message?.error('订单编号不存在');
  270. return;
  271. }
  272. await copyTextToClipboard(row.orderNumber);
  273. }
  274. function handleRefsh() {
  275. getData();
  276. getNums();
  277. }
  278. </script>
  279. <template>
  280. <div class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
  281. <NCard :bordered="false" size="small">
  282. <NCollapse display-directive="show">
  283. <NCollapseItem title="搜索">
  284. <BasicForm @register-form="registerSearchForm" @submit="handleSearch" @reset="handleReset" />
  285. </NCollapseItem>
  286. </NCollapse>
  287. </NCard>
  288. <NCard title="订单列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
  289. <NTabs v-model:value="activeTab" type="line" animated class="mb-16px h-full" display-directive="show">
  290. <NTabPane v-for="item in statusList" :key="item.value" :name="item.value" :tab="`${item.label}(${item.num})`">
  291. <NDataTable
  292. v-model:checked-row-keys="checkedRowKeys"
  293. :columns="columns"
  294. :data="data"
  295. size="small"
  296. :flex-height="!appStore.isMobile"
  297. :scroll-x="1800"
  298. :loading="loading"
  299. :row-key="row => row.orderId"
  300. remote
  301. class="sm:h-full"
  302. :pagination="mobilePagination"
  303. />
  304. </NTabPane>
  305. </NTabs>
  306. <NormalMoadl ref="orderMoadl" @finish="handleRefsh"></NormalMoadl>
  307. <DeliveryModal ref="Shipment" @finish="handleRefsh"></DeliveryModal>
  308. </NCard>
  309. </div>
  310. </template>
  311. <style scoped>
  312. :deep(.n-tabs-pane-wrapper) {
  313. height: 100%;
  314. }
  315. :deep(.n-tab-pane) {
  316. height: 100%;
  317. }
  318. </style>