index.vue 8.2 KB

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