Kaynağa Gözat

```
feat(order): 更新订单管理API接口和服务配置

- 更新API端点从/platform到/smqjh-oms/api/v1/order
- 修改请求方法为POST并调整参数传递方式
- 添加快递公司和物流追踪相关API函数
- 更新导出功能的URL和状态字段

refactor(user): 优化用户搜索表单组件属性

- 为搜索输入框添加占位符文本"请输入用户名/昵称/手机号"
- 调整组件props配置结构

feat(after-sales): 实现售后订单页面初始化逻辑

- 在页面挂载时自动设置查询参数和刷新表格
- 导入useRoute并注册表格刷新功能

refactor(normal-order): 移除未使用的导出工具函数

- 注释掉commonExport工具函数导入
- 调整表格注册配置去除未使用的方法

feat(normal-order): 增加所属企业列和搜索筛选

- 添加渠道企业搜索下拉选择器
- 在订单列表中显示所属企业信息
- 扩展搜索条件包括企业筛选

fix(normal-order): 修复导出功能和表格交互问题

- 修正导出列的时间和状态字段映射
- 更新下载链接处理方式为直接打开URL
- 调整表格刷新和加载状态控制

feat(normal-order): 增加订单详情和发货按钮功能

- 添加售后订单跳转按钮
- 实现发货流程按钮及路由导航
- 调整导出菜单触发方式为点击

refactor(normal-order): 重构搜索表单字段配置

- 重新组织搜索条件字段顺序
- 添加多个新的搜索筛选项如配送方式、业务状态等
- 优化表单验证和显示逻辑

feat(order-detail): 集成订单发货和物流追踪功能

- 添加订单发货表单弹窗实现
- 集成快递公司选择和物流节点追踪
- 实现发货状态更新和信息展示

chore(config): 更新测试环境API基础URL配置

- 切换默认测试服务器地址
- 启用本地测试服务器配置选项
- 注释掉旧的服务器配置

refactor(components): 更新组件类型声明

- 添加NCountdown组件类型定义
```

wenjie 3 gün önce
ebeveyn
işleme
7885375126

+ 3 - 3
.env.test

@@ -1,6 +1,6 @@
 # backend service base url, test environment
 
-# VITE_SERVICE_BASE_URL=https://b8dbdde.r39.cpolar.top #王
+# VITE_SERVICE_BASE_URL=https://522d2ea1.r39.cpolar.top #王
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846 #张
 # VITE_SERVICE_BASE_URL=http://192.168.1.89:8080#田
 # VITE_SERVICE_BASE_URL=https://425f86e6.r24.cpolar.top #邓
@@ -9,8 +9,8 @@
 # VITE_SERVICE_BASE_URL=https://735a1bda.r24.cpolar.top #黄
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846
 
-# VITE_SERVICE_BASE_URL=http://47.109.84.152:8081#打包测试本地服务器
-VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com #服务器
+VITE_SERVICE_BASE_URL=http://47.109.84.152:8081#打包测试本地服务器
+# VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com #服务器
 
 # other backend service base url, test environment
 VITE_OTHER_SERVICE_BASE_URL= `{

+ 32 - 8
src/service/api/order-manage/normal-order/index.ts

@@ -20,9 +20,10 @@ export function fetchGetDeliveryOrderList(data: any) {
  */
 export function fetchExportOrderList(data: any) {
   return request({
-    url: '/platform/order/export',
-    method: 'get',
-    params: data
+    url: '/smqjh-oms/api/v1/order/export',
+    method: 'post',
+    params: data,
+    data
   });
 }
 
@@ -33,21 +34,21 @@ export function fetchExportOrderList(data: any) {
  */
 export function fetchExportList(data: any) {
   return request({
-    url: '/platform/exportTask/page',
+    url: '/smqjh-oms/api/v1/order/export/records',
     method: 'get',
     params: data
   });
 }
 
 /**
- * 下载
+ * 中断下载
  * @param data
  * @returns
  */
 export function fetchBreakDownload(fileId: any) {
   return request({
-    url: `/platform/exportTask/cancel/${fileId}`,
-    method: 'get'
+    url: `/smqjh-oms/api/v1/order/export/interrupt/${fileId}`,
+    method: 'put'
   });
 }
 
@@ -93,7 +94,8 @@ export function fetchGetDevList(data: any) {
  */
 export function fetchDeliveryOrder(data: any) {
   return request({
-    url: '/platform/order/delivery',
+    // url: '/platform/order/delivery',
+    url: '/smqjh-oms/api/v1/order/delivery',
     method: 'PUT',
     data
   });
@@ -109,3 +111,25 @@ export function fetchAudit(data: any) {
     params: data
   });
 }
+
+/**
+ * 快递公司
+ */
+export function fetchExpressDeliveryList(data: any) {
+  return request({
+    url: '/smqjh-oms/api/v1/order/expressDeliveryList',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 物流追踪
+ */
+export function fetchDeliveryNode(data: any) {
+  return request({
+    url: '/smqjh-oms/api/v1/order/deliveryNode',
+    method: 'get',
+    params: data
+  });
+}

+ 9 - 0
src/service/api/xsb-manage/store-info/index.ts

@@ -1,5 +1,14 @@
 import { request } from '@/service/request';
 
+/** 获取所属企业列表 */
+export function fetchBackendChannelSelect(params?: any) {
+  return request<Api.System.DictDataList>({
+    url: `/smqjh-oms/api/v1/order/backendChannelSelect`,
+    method: 'get',
+    params
+  });
+}
+
 /**
  * 分页获取门店详细列表
  * @param data

+ 1 - 0
src/typings/components.d.ts

@@ -57,6 +57,7 @@ declare module 'vue' {
     NCollapse: typeof import('naive-ui')['NCollapse']
     NCollapseItem: typeof import('naive-ui')['NCollapseItem']
     NColorPicker: typeof import('naive-ui')['NColorPicker']
+    NCountdown: typeof import('naive-ui')['NCountdown']
     NDataTable: typeof import('naive-ui')['NDataTable']
     NDatePicker: typeof import('naive-ui')['NDatePicker']
     NDialogProvider: typeof import('naive-ui')['NDialogProvider']

+ 5 - 2
src/views/manage/user/index.vue

@@ -99,8 +99,11 @@ const [registerTable, { refresh, setTableLoading }] = useTable({
     schemas: [
       {
         field: 'keywords',
-        label: '用户名',
-        component: 'NInput'
+        label: '关键字',
+        component: 'NInput',
+        componentProps: {
+          placeholder: '请输入用户名/昵称/手机号'
+        }
       }
     ],
     inline: false,

+ 18 - 3
src/views/order-manage/after-sales-order/index.vue

@@ -1,5 +1,6 @@
 <script setup lang="tsx">
-import { useRouter } from 'vue-router';
+import { onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
 import { NTag } from 'naive-ui';
 import {
   // fetchAudit,
@@ -10,7 +11,14 @@ import {
 // import { copyTextToClipboard } from '@/utils/zt';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 // import { type } from '../../../../packages/axios/src/index';
-import { DJKOrderStatus, SearchForm, businessType, getSearchForm, orderStatus, refundOrderStatus } from '../normal-order/normal-order';
+import {
+  DJKOrderStatus,
+  businessType,
+  getSearchForm,
+  orderStatus,
+  refundOrderStatus
+} from '../normal-order/normal-order';
+const route = useRoute();
 const router = useRouter();
 // const ShipmentModal = useTemplateRef('Shipment');
 const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
@@ -105,7 +113,7 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
   }
 ];
 
-const [registerTable] = useTable({
+const [registerTable, { refresh, setFieldsValue }] = useTable({
   searchFormConfig: {
     schemas: [
       ...getSearchForm(),
@@ -149,6 +157,13 @@ function handleAfterSalesOrderDetail(row: Api.delivery.deliveryOrder) {
     }
   });
 }
+
+onMounted(() => {
+  setTimeout(async () => {
+    await setFieldsValue({ orderNumber: route.query.orderNumber });
+    refresh();
+  }, 1000);
+});
 </script>
 
 <template>

+ 105 - 54
src/views/order-manage/normal-order/index.vue

@@ -14,11 +14,11 @@ import {
 // import { fetchGetLoginUserList } from '@/service/api/common';
 // import { useAuth } from '@/hooks/business/auth';
 // import { copyTextToClipboard } from '@/utils/zt';
-import { commonExport } from '@/utils/common';
+// import { commonExport } from '@/utils/common';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 // import { type } from '../../../../packages/axios/src/index';
-import { DJKOrderStatus, SearchForm, businessType, getSearchForm, orderStatus } from './normal-order';
+import { DJKOrderStatus, businessType, getSearchForm, orderStatus } from './normal-order';
 const router = useRouter();
 const route = useRoute();
 const activeTab = ref('all');
@@ -52,6 +52,12 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
       return <NTag>{businessType[row.businessType as keyof typeof businessType] || row.businessType}</NTag>;
     }
   },
+  {
+    key: 'channelName',
+    title: '所属企业',
+    align: 'center',
+    width: 120
+  },
   {
     key: 'info',
     title: '客户信息',
@@ -102,7 +108,7 @@ const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
   }
 ];
 
-const [registerTable, { refresh, setTableLoading, getSeachForm, getTableData, setFieldsValue }] = useTable({
+const [registerTable, { refresh, getSeachForm, getTableData, setFieldsValue }] = useTable({
   searchFormConfig: {
     schemas: [...getSearchForm()]
   },
@@ -115,6 +121,17 @@ const [registerTable, { refresh, setTableLoading, getSeachForm, getTableData, se
   }
 });
 
+const [registerLogTable, { refresh: refresh1, setTableLoading }] = useTable({
+  tableConfig: {
+    keyField: 'id',
+    title: '',
+    showAddButton: false,
+    showTableHeaderAction: true,
+    showSearch: false,
+    minHeight: 400
+  }
+});
+
 const exportColumns: NaiveUI.TableColumn<InternalRowData>[] = [
   {
     key: 'index',
@@ -132,32 +149,23 @@ const exportColumns: NaiveUI.TableColumn<InternalRowData>[] = [
     minWidth: 100
   },
   {
-    key: 'updateTime',
+    key: 'createTime',
     title: '时间',
-    align: 'center',
-    minWidth: 100,
-    render: row => {
-      return (
-        <div>
-          <div>创建时间:{row.createTime}</div>
-          <div>完成时间:{row.updateTime}</div>
-        </div>
-      );
-    }
+    align: 'center'
   },
   {
-    key: 'operator',
+    key: 'operatorAccount',
     title: '操作人',
     align: 'center',
     minWidth: 100
   },
   {
-    key: 'exportStatus',
+    key: 'status',
     title: '状态',
     align: 'center',
     minWidth: 100,
     render: row => {
-      if (row.exportStatus == 0) {
+      if (row.status == 0) {
         return (
           <div>
             请耐心等待,正在导出中...
@@ -166,43 +174,46 @@ const exportColumns: NaiveUI.TableColumn<InternalRowData>[] = [
             </n-button>
           </div>
         );
-      } else if (row.exportStatus == 1) {
-        return (
-          <div>
-            未下载
-            <n-button text type="info" onClick={() => handleDownload(row.id as string)}>
-              下载
-            </n-button>
-          </div>
-        );
-      } else if (row.exportStatus == 2) {
-        return <div class={'text-gray-500'}>生成文件失败</div>;
-      } else if (row.exportStatus == 3) {
+      } else if (!row.fileUrl) {
+        return <div>导出失败</div>;
+      } else if (row.status == 1) {
         return (
           <div>
-            已下载&nbsp;
-            <n-button text type="info" onClick={() => handleDownload(row.id as string)}>
+            完成 {row.fileSize}
+            <n-button text type="info" onClick={() => handleDownload(row.fileUrl as string)}>
               下载
             </n-button>
           </div>
         );
-      } else if (row.exportStatus == 4) {
-        return <div>导出失败</div>;
       }
+      // else if (row.exportStatus == 2) {
+      //   return <div class={'text-gray-500'}>生成文件失败</div>;
+      // } else if (row.exportStatus == 3) {
+      //   return (
+      //     <div>
+      //       已下载&nbsp;
+      //       <n-button text type="info" onClick={() => handleDownload(row.id as string)}>
+      //         下载
+      //       </n-button>
+      //     </div>
+      //   );
+      // }
+
       return <div>进行中</div>;
     }
   }
 ];
 
-async function handleDownload(id: string) {
-  setTableLoading(true);
-  await commonExport('/platform/exportTask/download', { fileId: id }, '正常订单列表.xlsx');
-  refresh();
+async function handleDownload(fileUrl: string) {
+  // setTableLoading(true);
+  window.open(fileUrl);
+  // await commonExport('/platform/exportTask/download', { fileId: id }, '正常订单列表.xlsx');
+  refresh1();
 }
 async function handleBreak(id: string) {
   setTableLoading(true);
   await fetchBreakDownload(id);
-  refresh();
+  refresh1();
 }
 
 const [registerModalPrice, { openModal }] = useModal({
@@ -235,6 +246,18 @@ function handleOrderDetail(row: Api.delivery.deliveryOrder) {
   // orderMoadl.value?.open(String(row.orderNumber));
 }
 
+function handleSaleOrder(row: Api.delivery.deliveryOrder) {
+  if (!row.orderNumber) {
+    window.$message?.error('订单异常');
+  }
+  router.push({
+    path: '/order-manage/after-sales-order',
+    query: {
+      orderNumber: row.orderNumber
+    }
+  });
+}
+
 // async function handleAudit(row: any) {
 //   window.$dialog?.info({
 //     title: '提示',
@@ -306,7 +329,7 @@ getNums();
 //   await copyTextToClipboard(row[key]);
 // }
 
-async function handleExport() {
+async function handleExport(exportType: any) {
   setTableLoading(true);
   try {
     // await commonExport(
@@ -314,7 +337,7 @@ async function handleExport() {
     //   { ...getFieldsValue(), orderStatus: activeTab.value },
     //   '正常订单列表.xlsx'
     // );
-    await fetchExportOrderList({ ...getSeachForm(), orderStatus: activeTab.value });
+    await fetchExportOrderList({ exportType });
     dialog.success({
       title: '提示',
       content: () => {
@@ -366,11 +389,24 @@ function handleSelect(key: string) {
   console.log(key);
 
   if (key === 'goods') {
-    handleExport();
+    handleExport('PRODUCT');
   } else if (key === 'order') {
-    handleExportLog();
+    handleExport('ORDER');
   }
 }
+
+function handleship(row: Api.delivery.deliveryOrder) {
+  if (!row.orderNumber) {
+    window.$message?.error('订单异常');
+  }
+  router.push({
+    path: '/order-manage/order-detail',
+    query: {
+      orderNumber: row.orderNumber,
+      type: 'ship'
+    }
+  });
+}
 </script>
 
 <template>
@@ -384,7 +420,30 @@ function handleSelect(key: string) {
       @search="getNums"
     >
       <template #op="{ row }">
-        <NButton size="small" type="primary" ghost @click="handleOrderDetail(row)">订单详情</NButton>
+        <div>
+          <NButton size="small" type="primary" ghost @click="handleOrderDetail(row)">订单详情</NButton>
+          <NButton
+            v-if="row.backendOrderRefundButton"
+            class="mt-10px"
+            size="small"
+            type="primary"
+            ghost
+            @click="handleSaleOrder(row)"
+          >
+            售后订单
+          </NButton>
+
+          <NButton
+            v-if="row.dvyType === 10 && row.hbOrderStatus === 40"
+            class="mt-10px"
+            size="small"
+            type="primary"
+            ghost
+            @click="handleship(row)"
+          >
+            {{ row.dvyno ? '修改发货信息' : '发货' }}
+          </NButton>
+        </div>
         <!--
  <NButton
           v-if="row.businessType === 'DJK' && row.isWriteOff == 1 && row.hbOrderStatus != 80"
@@ -417,15 +476,8 @@ function handleSelect(key: string) {
       </template>
       <template #prefix="{ loading }">
         <div class="flex items-center">
-          <NDropdown trigger="hover" :options="options" @select="handleSelect">
-            <NButton
-              size="small"
-              type="primary"
-              ghost
-              :loading="loading"
-              :disabled="getData().length === 0"
-              @click="handleExport"
-            >
+          <NDropdown trigger="click" :options="options" @select="handleSelect">
+            <NButton size="small" type="primary" ghost :loading="loading" :disabled="getData().length === 0">
               <template #icon>
                 <SvgIcon icon="mingcute:file-export-line"></SvgIcon>
               </template>
@@ -445,8 +497,7 @@ function handleSelect(key: string) {
           :show-table-action="false"
           :columns="exportColumns"
           :api="fetchExportList"
-          :default-params="{ exportType: 1, orderNumber }"
-          @register="registerTable"
+          @register="registerLogTable"
         ></ZTable>
       </LayoutTable>
     </BasicModal>

+ 239 - 89
src/views/order-manage/normal-order/normal-order.ts

@@ -1,10 +1,11 @@
 import { h } from 'vue';
 import { NFlex, NImage, NTag } from 'naive-ui';
 // import { fetchGetAllStoreList } from '@/service/api/goods/desk-category';
-import { fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
+import { fetchBackendChannelSelect, fetchGetStoreList } from '@/service/api/xsb-manage/store-info';
 import { fetchGetDictDataList } from '@/service/api/system-manage';
 // import { useAuth } from '@/hooks/business/auth';
 import { useAuthStore } from '@/store/modules/auth';
+import { useAuth } from '@/hooks/business/auth';
 import type { FormSchema } from '@/components/zt/Form/types/form';
 
 export const SearchForm: FormSchema[] = [
@@ -22,6 +23,17 @@ export const SearchForm: FormSchema[] = [
       }
     }
   },
+  {
+    label: '所属企业',
+    field: 'channelIdList',
+    component: 'ApiSelect',
+    componentProps: {
+      api: fetchBackendChannelSelect,
+      multiple: true,
+      labelFeild: 'name',
+      valueFeild: 'id'
+    }
+  },
   {
     label: '门店名称',
     component: 'ApiSelect',
@@ -34,93 +46,183 @@ export const SearchForm: FormSchema[] = [
     }
   },
 
-  // {
-  //   label: '订单类型',
-  //   component: 'NSelect',
-  //   field: 'dvyType',
-  //   componentProps: {
-  //     options: [
-  //       {
-  //         label: '全部',
-  //         value: ''
-  //       },
-  //       {
-  //         label: '快递',
-  //         value: 1
-  //       },
-  //       {
-  //         label: '配送',
-  //         value: 3
-  //       }
-  //     ]
-  //   }
-  // },
-  // {
-  //   label: '父订单编号',
-  //   component: 'NInput',
-  //   field: 'parentOrderNumber'
-  // },
   {
     label: '订单编号',
     component: 'NInput',
     field: 'orderNumber'
   },
   {
-    label: '收货人姓名',
+    label: '下单时间',
+    component: 'NDatePicker',
+    field: 'createTime',
+    componentProps: {
+      type: 'datetimerange',
+      defaultTime: ['00:00:00', '23:59:59']
+    }
+  },
+  {
+    label: '客户姓名',
     component: 'NInput',
     field: 'consigneeName'
   },
   {
-    label: '收货人电话',
+    label: '客户手机号',
     component: 'NInput',
     field: 'consigneeMobile'
   },
-  // {
-  //   label: '售后状态',
-  //   component: 'NSelect',
-  //   field: 'refundStatus',
-  //   componentProps: {
-  //     options: [
-  //       {
-  //         label: '申请退款',
-  //         value: 1
-  //       },
-  //       {
-  //         label: '退款成功',
-  //         value: 2
-  //       },
-  //       {
-  //         label: '部分退款成功',
-  //         value: 3
-  //       },
-  //       {
-  //         label: '退款失败',
-  //         value: 4
-  //       }
-  //     ]
-  //   }
-  // },
   {
-    label: '下单时间',
-    component: 'NDatePicker',
-    field: 'createTime',
+    label: '买家姓名',
+    component: 'NInput',
+    field: 'buyerName'
+  },
+  {
+    label: '买家手机号',
+    component: 'NInput',
+    field: 'buyerMobile'
+  },
+  {
+    field: 'userAttrType',
+    label: '买家属性',
+    component: 'dictSelect',
     componentProps: {
-      type: 'datetimerange',
-      defaultTime: ['00:00:00', '23:59:59']
+      dictCode: 'user_attr_type',
+      immediate: true
+    },
+    show: useAuth().hasAuth('user:attr:type')
+  },
+  {
+    label: '退款类型',
+    component: 'NSelect',
+    field: 'refundStatus',
+    componentProps: {
+      options: [
+        {
+          label: '整单退款',
+          value: 2
+        },
+        {
+          label: '部分退款',
+          value: 1
+        },
+        {
+          label: '无售后订单',
+          value: 0
+        }
+      ]
+    }
+  },
+  {
+    label: '发货情况',
+    component: 'NSelect',
+    field: 'deliveryStatus',
+    componentProps: {
+      options: [
+        {
+          label: '已发货',
+          value: 'Shipped'
+        },
+        {
+          label: '未发货',
+          value: 'NoShipped'
+        },
+        {
+          label: '未发货-无需发货',
+          value: 'NoNeedShipped'
+        }
+      ]
+    },
+    ifShow({ model }) {
+      return model.businessType == 'XSB';
+    }
+  },
+  {
+    label: '配送方式',
+    component: 'NSelect',
+    field: 'dvyTypeList',
+    componentProps: {
+      multiple: true,
+      options: [
+        {
+          label: '快递',
+          value: 1
+        },
+        {
+          label: '自提',
+          value: 2
+        },
+        {
+          label: '即时配送',
+          value: 3
+        },
+        {
+          label: '其他方式',
+          value: 4
+        },
+        {
+          label: '商家自送',
+          value: 10
+        }
+      ]
+    },
+    ifShow({ model }) {
+      return model.businessType == 'XSB';
+    }
+  },
+  {
+    label: '业务状态',
+    component: 'NSelect',
+    field: 'hbOrderStatus',
+    componentProps: {
+      options: [
+        {
+          label: '待支付',
+          value: 0
+        },
+        {
+          label: '订单已接单(待拣货)',
+          value: 20
+        },
+        {
+          label: '订单待配送',
+          value: 30
+        },
+        {
+          label: '订单配送中',
+          value: 40
+        },
+        {
+          label: '订单取消待审核',
+          value: 50
+        },
+        {
+          label: '订单取消',
+          value: 60
+        },
+        {
+          label: '订单已送达',
+          value: 70
+        },
+        {
+          label: '订单已完成',
+          value: 80
+        }
+      ]
+    },
+    ifShow({ model }) {
+      return model.businessType == 'XSB';
     }
   }
-  // {
-  //   field: 'userAttrType',
-  //   label: '人员属性',
-  //   component: 'dictSelect',
-  //   componentProps: {
-  //     dictCode: 'user_attr_type',
-  //     immediate: true
-  //   },
-  //   show: useAuth().hasAuth('user:attr:type')
-  // }
 ];
 
+export const businessType = {
+  XSB: '星闪豹',
+  CD: '充电',
+  DYY: '电影演出',
+  DJK: '大健康',
+  XNSP: '虚拟商品',
+  JY: '加油'
+};
+
 /**
  * 获取搜索表单schema,业务类型下拉框根据当前用户权限过滤
  * ROOT用户:显示全量字典选项
@@ -338,28 +440,85 @@ export const orderColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[]
   },
   {
     title: '单价(元)',
-    key: 'price',
-    width: 100
+    key: 'price'
+    // width: 100
   },
   {
     title: '数量',
     key: 'prodCount',
-    width: 250,
+    // width: 250,
     render: row => {
       const nodes = [h('div', { class: 'mr-2' }, row.prodCount)];
-      if (row.refundSuccessCount) {
-        nodes.push(h(NTag, { class: 'ml2', type: 'success' }, () => `退款成功:${row.refundSuccessCount}`));
-      }
-      if (row.refundIngCount && !row.refundSuccessCount) {
-        nodes.push(h(NTag, { class: 'ml2', type: 'error' }, () => `售后处理:${row.refundIngCount}`));
-      }
+      // if (row.refundSuccessCount) {
+      //   nodes.push(h(NTag, { class: 'ml2', type: 'success' }, () => `退款成功:${row.refundSuccessCount}`));
+      // }
+      // if (row.refundIngCount && !row.refundSuccessCount) {
+      //   nodes.push(h(NTag, { class: 'ml2', type: 'error' }, () => `售后处理:${row.refundIngCount}`));
+      // }
       return h(NFlex, { align: 'center' }, () => nodes);
     }
   },
+  {
+    title: '退款成功数量',
+    key: 'refundSuccessCount'
+  },
   {
     title: '小计/元',
-    key: 'productTotalAmount',
-    width: 100
+    key: 'productTotalAmount'
+  }
+];
+
+export const orderShipColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    title: '商品',
+    key: 'goods',
+    width: 300,
+    render: row => {
+      const goodsNodes = [
+        h(NImage, { src: row.pic, width: 80, height: 80 }),
+        h('div', { class: 'ml-2 ' }, [
+          h('div', { class: 'text-15px font-semibold' }, row.skuName),
+          h('div', { class: 'text-gray' }, `规格:${row.spec || '--'}`)
+        ])
+      ];
+      return h('div', { class: 'flex items-center' }, goodsNodes);
+    }
+  },
+  {
+    title: '原商品数量',
+    key: 'prodCount',
+    width: 120,
+    render: row => {
+      const nodes = [h('div', { class: 'mr-2' }, row.prodCount)];
+      // if (row.refundSuccessCount) {
+      //   nodes.push(h(NTag, { class: 'ml2', type: 'success' }, () => `退款成功:${row.refundSuccessCount}`));
+      // }
+      // if (row.refundIngCount && !row.refundSuccessCount) {
+      //   nodes.push(h(NTag, { class: 'ml2', type: 'error' }, () => `售后处理:${row.refundIngCount}`));
+      // }
+      return h(NFlex, { align: 'center' }, () => nodes);
+    }
+  },
+  {
+    title: '退款成功数量',
+    width: 120,
+    key: 'refundSuccessCount'
+  },
+  {
+    title: '状态',
+    key: 'status',
+    width: 120,
+    render: () => {
+      return '等待发货';
+    }
+  },
+  {
+    title: '可发货数量',
+    key: 'prodCount',
+    width: 120,
+    render: row => {
+      return h('div', { class: '' }, Number(row.prodCount) - Number(row.refundSuccessCount));
+    }
   }
 ];
 
@@ -524,15 +683,6 @@ export const chargeMethod = [
   '其它'
 ];
 
-export const businessType = {
-  XSB: '星闪豹',
-  CD: '充电',
-  DYY: '电影演出',
-  DJK: '大健康',
-  XNSP: '虚拟商品',
-  JY: '加油'
-};
-
 export const payType = {
   0: '积分支付',
   1: '微信支付',

+ 245 - 30
src/views/order-manage/order-detail/index.vue

@@ -1,18 +1,27 @@
-<script setup lang="ts">
-import { ref } from 'vue';
+<script setup lang="tsx">
+import { onMounted, ref } from 'vue';
 import { useRoute } from 'vue-router';
 // import { NFlex } from 'naive-ui';
+import { NDataTable } from 'naive-ui';
 import dayjs from 'dayjs';
 import duration from 'dayjs/plugin/duration';
-import { fetchGetNomalOrderInfo } from '@/service/api/order-manage/normal-order';
+import {
+  fetchDeliveryNode,
+  fetchDeliveryOrder,
+  fetchExpressDeliveryList,
+  fetchGetNomalOrderInfo
+} from '@/service/api/order-manage/normal-order';
+import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
 // import { useAppStore } from '@/store/modules/app';
 // import { copyTextToClipboard } from '@/utils/zt';
 import {
   businessType,
   chargeMethod,
+  dvyStatus,
   orderColumns,
   orderDJKColumns,
   orderDJKLogColumns,
+  orderShipColumns,
   orderStatus,
   orderStatusEnum,
   payType,
@@ -24,22 +33,142 @@ const orderInfo = ref<Api.delivery.deliveryOrder>();
 const TimeDown = ref<number>(0);
 dayjs.extend(duration);
 const route = useRoute();
+const deliveryNode = ref<any[]>([]);
+const [
+  registerModalForm,
+  {
+    openModal: openModalForm,
+    setFieldsValue: setModalFormValue,
+    getFieldsValue: getModalFormValue,
+    closeModal: closeModalForm,
+    setSubLoading
+  }
+] = useModalFrom({
+  modalConfig: {
+    title: '订单发货',
+    isShowHeaderText: false,
+    width: 1000,
+    height: 700
+  },
+  formConfig: {
+    schemas: [
+      {
+        field: 'orderNumber',
+        label: '',
+        component: 'NInput',
+        render: () => {
+          return (
+            <div>
+              <div class="py-20px font-semibold">商品信息</div>
+              <NDataTable columns={orderShipColumns} data={orderInfo.value?.orderItemList} bordered={false} />
+
+              <div class="py-20px font-semibold">订单信息</div>
+              <div>配送方式:{dvyStatus[orderInfo.value?.dvyType as keyof typeof dvyStatus]}</div>
+              <div>收货人姓名:{orderInfo.value?.consigneeName}</div>
+              <div>收货人手机号:{orderInfo.value?.consigneeMobile}</div>
+              <div>收货地址:{orderInfo.value?.consigneeAddress}</div>
+            </div>
+          );
+        }
+      },
+      {
+        field: 'orderNumber',
+        label: '单号',
+        component: 'NInput',
+        show: false
+      },
+
+      {
+        field: 'deliveryType',
+        component: 'NRadioGroup',
+        label: '发货方式',
+        required: true,
+        defaultValue: 1,
+        componentProps: {
+          options: [
+            {
+              label: '快递',
+              value: 0
+            },
+            {
+              label: '其他',
+              value: 1
+            }
+          ]
+        }
+      },
+      {
+        label: '快递公司',
+        field: 'dvyId',
+        required: true,
+        component: 'ApiSelect',
+        componentProps: {
+          api: fetchExpressDeliveryList,
+          labelFeild: 'name',
+          valueFeild: 'id'
+        },
+
+        ifShow({ model }) {
+          return model.deliveryType === 0;
+        }
+      },
+      {
+        field: 'dvyFlowId',
+        label: '快递单号',
+        required: true,
+        component: 'NInput',
+
+        ifShow({ model }) {
+          return model.deliveryType === 0;
+        }
+      },
+      {
+        field: 'remark',
+        label: '备注',
+        required: true,
+        component: 'NInput',
+        ifShow({ model }) {
+          return model.deliveryType === 1;
+        }
+      }
+    ],
+    labelWidth: 80,
+    layout: 'horizontal',
+    gridProps: {
+      cols: '1',
+      itemResponsive: true
+    }
+  }
+});
+
 async function open(orderNumber: string) {
   const { data, error } = await fetchGetNomalOrderInfo(orderNumber);
   if (!error) {
     orderInfo.value = data;
-    if (orderInfo.value.hbLogisticStatus == orderStatusEnum.WAIT_PAY) {
+    if (orderInfo.value.hbOrderStatus == orderStatusEnum.WAIT_PAY) {
       const createTime = dayjs(orderInfo.value.createTime);
       const currentTime = dayjs();
       const elapsed = currentTime.diff(createTime);
       const fifteenMinutesInMillis = 15 * 60 * 1000;
       TimeDown.value = fifteenMinutesInMillis - elapsed;
+    } else if (
+      orderInfo.value.hbOrderStatus !== orderStatusEnum.ORDER_DELIVERY &&
+      orderInfo.value.businessType == 'XSB'
+    ) {
+      getDeliveryNode();
     }
   }
 }
 console.log(route.query.orderNumber);
+async function getDeliveryNode() {
+  const { data, error } = await fetchDeliveryNode({ orderNumber: String(route.query.orderNumber) });
+
+  if (!error) {
+    deliveryNode.value = data;
+    console.log(data, '节点信息');
+  }
+}
 
-open(String(route.query.orderNumber));
 defineExpose({ open });
 
 // const isRefund = computed(() => {
@@ -88,6 +217,46 @@ function secondsToTime(seconds: number) {
   }
   return `00:${formatTime(minutes)}:${formatTime(secs)}`;
 }
+
+function handleShip() {
+  openModalForm();
+  setModalFormValue({
+    orderNumber: orderInfo.value?.orderNumber,
+    dvyId: orderInfo.value?.dvyId,
+    dvyFlowId: orderInfo.value?.dvyFlowId,
+    deliveryType: orderInfo.value?.deliveryType || 0,
+    remark: orderInfo.value?.dvyName
+  });
+}
+
+async function handleSubmit() {
+  const form = await getModalFormValue();
+  if (form.deliveryType == 0) {
+    delete form.remark;
+  } else {
+    delete form.dvyId;
+    delete form.dvyFlowId;
+  }
+  console.log(form, '表单');
+  const { error } = await fetchDeliveryOrder(form);
+  if (!error) {
+    window.$message?.success('保存成功');
+  }
+  setSubLoading(false);
+  closeModalForm();
+  open(String(route.query.orderNumber));
+}
+
+function handleFinish() {
+  open(String(orderInfo.value?.orderNumber));
+}
+
+onMounted(async () => {
+  await open(String(route.query.orderNumber));
+  if (route.query.type == 'ship') {
+    handleShip();
+  }
+});
 </script>
 
 <template>
@@ -112,12 +281,12 @@ function secondsToTime(seconds: number) {
             订单状态:
             <template v-if="orderInfo.businessType != 'DYY'">
               {{
-                orderInfo.hbOrderStatus == 20 ||
-                orderInfo.hbOrderStatus == 30 ||
-                orderInfo.hbOrderStatus == 40 ||
-                orderInfo.hbOrderStatus == 70
-                  ? '进行中'
-                  : orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus]
+              orderInfo.hbOrderStatus == 20 ||
+              orderInfo.hbOrderStatus == 30 ||
+              orderInfo.hbOrderStatus == 40 ||
+              orderInfo.hbOrderStatus == 70
+              ? '进行中'
+              : orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus]
               }}
             </template>
             <template v-else>
@@ -142,7 +311,9 @@ function secondsToTime(seconds: number) {
         </NCard>
         <NCard size="small" class="mt-20px" title="支付信息" :bordered="false">
           <div>支付状态:{{ orderInfo.isPayed ? '已支付' : '未支付' }}</div>
-          <div>支付金额:{{ orderInfo.actualTotal }}元</div>
+          <div v-if="orderInfo.businessType == 'JY'">支付金额:{{ orderInfo.omsOrderOilVO?.realMoney || '---' }}元</div>
+
+          <div v-else>支付金额:{{ orderInfo.actualTotal }}元</div>
           <div>
             付款方式:{{
               orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY ||
@@ -157,17 +328,34 @@ function secondsToTime(seconds: number) {
         </NCard>
       </div>
       <div class="flex-1">
-        <div class="flex">
-          <div class="mb-10px text-16px font-semibold">
-            业务状态:
-            <template v-if="orderInfo.businessType != 'DYY'">
-              {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
-            </template>
-            <template v-else>
-              {{ yppStatus[orderInfo.yppDetail?.orderState as keyof typeof yppStatus] }}
-            </template>
+        <div class="flex justify-between">
+          <div class="flex">
+            <div class="mb-10px text-16px font-semibold">
+              业务状态:
+              <template v-if="orderInfo.businessType != 'DYY'">
+                {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
+                {{ orderInfo.deliveryType && orderInfo.deliveryType == 1 ? '(已发货 其他方式)' : '' }}
+              </template>
+              <template v-else>
+                {{ yppStatus[orderInfo.yppDetail?.orderState as keyof typeof yppStatus] }}
+              </template>
+            </div>
+            <div v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY" class="text-gray">
+              如买家未在
+              <NTag :type="TimeDown > 300094 ? 'success' : 'error'">
+                <NCountdown :duration="TimeDown" @finish="handleFinish" />
+              </NTag>
+              内付款,订单将 自动关闭。
+            </div>
+            <div v-else class="mb-10px ml-20px text-16px font-semibold">
+              第三方订单编号:{{ orderInfo.thirdOrderId }}
+            </div>
           </div>
-          <div class="mb-10px ml-20px text-16px font-semibold">第三方订单编号:{{ orderInfo.thirdOrderId }}</div>
+
+          <!-- v-if="orderInfo.dvyType === 10 && orderInfo.hbOrderStatus === 40" -->
+          <NButton size="small" type="primary" @click="handleShip">
+            {{ orderInfo.dvyNo ? '修改发货信息' : '发货' }}
+          </NButton>
         </div>
         <NCard size="small" title="业务信息" :bordered="false">
           <template v-if="orderInfo.businessType == 'XSB'">
@@ -213,6 +401,20 @@ function secondsToTime(seconds: number) {
                 </NTr>
               </NTbody>
             </NTable>
+
+            <template v-if="orderInfo.dvyType == 3 && orderInfo.hbOrderStatus != orderStatusEnum.WAIT_PAY">
+              <div class="py-20px font-semibold">03 订单追踪</div>
+              <div v-if="orderInfo.deliveryType == 1">信息记录:{{ orderInfo.dvyName || '暂无' }}</div>
+              <NTimeline v-else>
+                <NTimelineItem
+                  v-for="(item, index) in deliveryNode"
+                  :key="item.id"
+                  :type="index == 0 ? 'success' : undefined"
+                  :content="item.content"
+                  :time="item.updateTime"
+                />
+              </NTimeline>
+            </template>
           </template>
           <template v-else-if="orderInfo.businessType == 'DYY'">
             <div class="pb-20px font-semibold">01 影片与场次信息</div>
@@ -286,6 +488,7 @@ function secondsToTime(seconds: number) {
           <template v-else-if="orderInfo.businessType == 'CD'">
             <div class="flex flex-wrap items-center">
               <div class="mr10px flex-shrink-0">
+                <div>车牌号:{{ orderInfo.chargeOrder.plateNum || '--' }}</div>
                 <div>终端编号:{{ orderInfo.chargeOrder.connectorId || '--' }}</div>
                 <div>充电电站:{{ orderInfo.chargeOrder.powerStationName || '--' }}</div>
                 <div>充电开始时间:{{ orderInfo.chargeOrder.startTime || '--' }}</div>
@@ -323,8 +526,18 @@ function secondsToTime(seconds: number) {
                   </NTr>
                   <NTr>
                     <NTd>积分抵扣</NTd>
-                    <NTd>{{ orderInfo.actualTotal }}</NTd>
-                    <!-- <NTd>{{ orderInfo.actualTotal - orderInfo.platformVolume }}</NTd> -->
+                    <!-- <NTd>{{ orderInfo.actualTotal }}</NTd> -->
+                    <NTd>
+                      -{{
+                        (
+                          Number(orderInfo?.actualTotal || 0) - Number(orderInfo?.chargeOrder?.platformVolume || 0)
+                        ).toFixed(2)
+                      }}
+                    </NTd>
+                  </NTr>
+                  <NTr>
+                    <NTd>平台券</NTd>
+                    <NTd>-{{ Number(orderInfo?.chargeOrder?.platformVolume || 0) }}</NTd>
                   </NTr>
                 </NTbody>
               </NTable>
@@ -421,12 +634,12 @@ function secondsToTime(seconds: number) {
             <div>加油量:{{ orderInfo.omsOrderOilVO?.quantity + 'L' || '---' }}</div>
 
             <div class="mt-20px pb-20px font-semibold">04 加油明细</div>
-            <div>门店价:{{ orderInfo.omsOrderOilVO?.storePrice || '---' }}</div>
-            <div>平台价:{{ orderInfo.omsOrderOilVO?.vipPrice || '---' }}</div>
-            <div>订单金额:{{ orderInfo.omsOrderOilVO?.totalMoney || '---' }}</div>
-            <div>抵扣金额:{{ orderInfo.omsOrderOilVO?.promotionAmount || '---' }}</div>
-            <div>服务费:{{ orderInfo.omsOrderOilVO?.serviceFee || '---' }}</div>
-            <div>实付金额:{{ orderInfo.omsOrderOilVO?.realMoney || '---' }}</div>
+            <div>门店价:{{ orderInfo.omsOrderOilVO?.storePrice || '---' }}/L</div>
+            <div>平台价:{{ orderInfo.omsOrderOilVO?.vipPrice || '---' }}/L</div>
+            <div>订单金额:{{ orderInfo.omsOrderOilVO?.totalMoney || '---' }}</div>
+            <div>抵扣金额:-¥{{ orderInfo.omsOrderOilVO?.promotionAmount || '---' }}</div>
+            <div>服务费:+¥{{ orderInfo.omsOrderOilVO?.serviceFee || '---' }}</div>
+            <div>实付金额:{{ orderInfo.omsOrderOilVO?.realMoney || '---' }}</div>
 
             <div class="mt-20px pb-20px font-semibold">05 抵扣信息</div>
             <div>抵扣券ID:{{ orderInfo.omsOrderOilVO?.allowanceId || '---' }}</div>
@@ -435,6 +648,8 @@ function secondsToTime(seconds: number) {
         </NCard>
       </div>
     </div>
+
+    <BasicModelForm @register-modal-form="registerModalForm" @submit-form="handleSubmit"></BasicModelForm>
   </div>
 </template>