ソースを参照

```
feat(router): 添加拆单相关路由配置

新增拆单订单和拆单详情页面的路由配置,包括对应的国际化路由映射和组件导入,
完善交付模块的功能覆盖。

feat(i18n): 添加拆单功能国际化配置

在中英文语言包中添加拆单相关页面的国际化键值对,确保新功能的语言支持。

feat(delivery): 实现拆单功能相关API和服务

- 更新订单分割相关接口定义
- 添加拆单订单列表和详情页面
- 实现拆单功能的核心业务逻辑
- 新增订单复制功能以支持父订单和子订单操作

feat(user-management): 实现用户评论管理功能

新增用户评论管理页面,包含评论展示、审核、回复等功能,支持多维度筛选和状态管理。

fix(auth): 修复登录token字段映射问题

修正登录响应中的刷新令牌字段名从refreshToken到refresh_token,
确保认证流程中令牌存储的一致性。

feat(normal-order): 增强订单列表展示功能

- 添加父订单和子订单编号显示
- 实现手动拆单入口
- 改进订单复制功能
- 优化导出功能参数处理

refactor(env): 更新测试环境配置

替换过期的测试服务器地址,启用新的cpolar隧道地址以确保测试环境可用性。

chore(typings): 更新API类型定义

同步更新API类型定义文件,包括认证令牌字段名变更和新增的用户评论相关接口类型。

feat(table): 添加NRate组件类型声明

为naive-ui的NRate组件添加类型声明,支持评分功能的类型检查。

fix(after-sales-order): 修复导出功能时间参数处理

修正售后订单导出时的时间范围参数处理逻辑,确保正确传递开始和结束时间。

docs(normal-modal): 更新订单详情显示逻辑

调整订单详情弹窗中父订单和子订单的标题显示逻辑,优化用户体验。

style(hot-search): 重构热词搜索组件为用户评论管理

将原有的热词搜索功能迁移为用户评论管理功能,实现功能重用和代码清理。
```

wenjie 2 週間 前
コミット
7aa0566d4a

+ 3 - 3
.env.test

@@ -1,13 +1,13 @@
 # backend service base url, test environment
 # VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun
-# VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun #付
+# VITE_SERVICE_BASE_URL=http://22b0cfad.r36.cpolar.top #付
 # VITE_SERVICE_BASE_URL=http://192.168.0.157:8114 #王
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846 #张
 # VITE_SERVICE_BASE_URL=http://192.168.1.66:8114 #邓
 # VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
 # VITE_SERVICE_BASE_URL=https://shop.platform.zswlgz.com #服务器
-VITE_SERVICE_BASE_URL=/plt #测试打包服务器
-# VITE_SERVICE_BASE_URL=http://47.109.84.152:8114 #测试本地服务器
+# VITE_SERVICE_BASE_URL=/plt #测试打包服务器
+VITE_SERVICE_BASE_URL=http://47.109.84.152:8114 #测试本地服务器
 
 
 # other backend service base url, test environment

+ 2 - 0
src/locales/langs/en-us.ts

@@ -303,6 +303,8 @@ const local: App.I18n.Schema = {
     'config_order-split': '',
     delivery: '',
     'delivery_normal-order': '',
+    'delivery_split-order': '',
+    'delivery_split-order-detail': '',
     'operation_delivery-admin': '',
     'delivery_after-sales-order': '',
     finance: '',

+ 2 - 0
src/locales/langs/zh-cn.ts

@@ -300,6 +300,8 @@ const local: App.I18n.Schema = {
     'config_order-split': '',
     delivery: '',
     'delivery_normal-order': '',
+    'delivery_split-order': '',
+    'delivery_split-order-detail': '',
     'operation_delivery-admin': '',
     'delivery_after-sales-order': '',
     finance: '',

+ 2 - 0
src/router/elegant/imports.ts

@@ -26,6 +26,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   "config_order-split": () => import("@/views/config/order-split/index.vue"),
   "delivery_after-sales-order": () => import("@/views/delivery/after-sales-order/index.vue"),
   "delivery_normal-order": () => import("@/views/delivery/normal-order/index.vue"),
+  "delivery_split-order-detail": () => import("@/views/delivery/split-order-detail/index.vue"),
+  "delivery_split-order": () => import("@/views/delivery/split-order/index.vue"),
   "finance_commodity-freight": () => import("@/views/finance/commodity-freight/index.vue"),
   finance_summary: () => import("@/views/finance/summary/index.vue"),
   "goods_desk-category": () => import("@/views/goods/desk-category/index.vue"),

+ 18 - 0
src/router/elegant/routes.ts

@@ -114,6 +114,24 @@ export const generatedRoutes: GeneratedRoute[] = [
           title: 'delivery_normal-order',
           i18nKey: 'route.delivery_normal-order'
         }
+      },
+      {
+        name: 'delivery_split-order',
+        path: '/delivery/split-order',
+        component: 'view.delivery_split-order',
+        meta: {
+          title: 'delivery_split-order',
+          i18nKey: 'route.delivery_split-order'
+        }
+      },
+      {
+        name: 'delivery_split-order-detail',
+        path: '/delivery/split-order-detail',
+        component: 'view.delivery_split-order-detail',
+        meta: {
+          title: 'delivery_split-order-detail',
+          i18nKey: 'route.delivery_split-order-detail'
+        }
       }
     ]
   },

+ 2 - 0
src/router/elegant/transform.ts

@@ -189,6 +189,8 @@ const routeMap: RouteMap = {
   "delivery": "/delivery",
   "delivery_after-sales-order": "/delivery/after-sales-order",
   "delivery_normal-order": "/delivery/normal-order",
+  "delivery_split-order": "/delivery/split-order",
+  "delivery_split-order-detail": "/delivery/split-order-detail",
   "finance": "/finance",
   "finance_commodity-freight": "/finance/commodity-freight",
   "finance_summary": "/finance/summary",

+ 37 - 0
src/service/api/delivery/split-order-detail/index.ts

@@ -0,0 +1,37 @@
+import { request } from '@/service/request';
+
+/**
+ * 确定拆单
+ * @param data
+ * @returns
+ */
+export function confirmSplit(data: any) {
+  return request({
+    url: '/platform/order/confirmSplit',
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 手动拆单处理
+ * @param data
+ * @returns
+ */
+export function manualSplit(data: any) {
+  return request({
+    url: '/platform/order/manualSplit',
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 删除临时子订单
+ */
+export function deleteTempSubOrder(id: string) {
+  return request({
+    url: `/platform/order/deleteTempSubOrder?subOrderNumber=${id}`,
+    method: 'DELETE'
+  });
+}

+ 38 - 0
src/service/api/user-management/user-reviews/index.ts

@@ -0,0 +1,38 @@
+import { request } from '@/service/request';
+
+/**
+ * 分页获取用户评论列表
+ * @param params
+ * @returns
+ */
+export function fetchGetBackendCommList(params: any) {
+  return request<Api.userManagement.userReviews[]>({
+    url: '/platform/prodComm/backendCommList',
+    method: 'get',
+    params
+  });
+}
+
+/**
+ * 审核用户评论
+ * @param params
+ */
+export function fetchGetBackendCommAudit(params: any) {
+  return request({
+    url: '/platform/prodComm/backendCommAudit',
+    method: 'get',
+    params
+  });
+}
+
+/**
+ *后台-商家回复
+ * @param params
+ */
+export function fetchGetReply(params: any) {
+  return request({
+    url: '/platform/prodComm/reply',
+    method: 'get',
+    params
+  });
+}

+ 1 - 1
src/service/request/shared.ts

@@ -18,7 +18,7 @@ async function handleRefreshToken() {
   const { error, data } = await fetchRefreshToken(rToken);
   if (!error) {
     localStg.set('token', data.token);
-    localStg.set('refreshToken', data.refreshToken);
+    localStg.set('refreshToken', data.refresh_token);
     return true;
   }
 

+ 6 - 2
src/store/modules/auth/index.ts

@@ -114,8 +114,9 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 
     const loginToken: Api.Auth.LoginToken = {
       token: response.data.access_token,
-      refreshToken: response.data.refreshToken
+      refresh_token: response.data.refresh_token
     };
+
     if (!error) {
       const pass = await loginByToken(loginToken);
 
@@ -146,7 +147,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
   async function loginByToken(loginToken: Api.Auth.LoginToken) {
     // 1. stored in the localStorage, the later requests need it in headers
     localStg.set('token', loginToken.token);
-    localStg.set('refreshToken', loginToken.refreshToken);
+    localStg.set('refreshToken', loginToken.refresh_token);
 
     // 2. get user info
     const pass = await getUserInfo();
@@ -162,6 +163,9 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 
   async function getUserInfo() {
     const { data: info, error } = await fetchGetUserInfo();
+    if (info) {
+      localStg.set('lastLoginUserId', info.userId);
+    }
     if (!error) {
       // update store
       // Object.assign(userInfo, info);

+ 19 - 1
src/typings/api.d.ts

@@ -60,7 +60,7 @@ declare namespace Api {
   namespace Auth {
     interface LoginToken {
       token: string;
-      refreshToken: string;
+      refresh_token: string;
     }
 
     interface UserInfo {
@@ -948,6 +948,22 @@ declare namespace Api {
        */
       channelId: number;
     }
+    interface userReviews {
+      avatar: string;
+      /**
+       * 头像
+       */
+      userName: string;
+      /**
+       * 回复内容
+       */
+      content: string;
+      // 昵称
+      nickName: string;
+      // 评分
+      score: number;
+      [property: string]: any;
+    }
   }
   namespace delivery {
     interface Transport2 {
@@ -1120,6 +1136,8 @@ declare namespace Api {
        */
       orderId?: number;
       orderItems?: OrderItemElement[];
+      subOrders?: OrderItemElement[];
+
       /**
        * 订购流水号
        */

+ 1 - 1
src/typings/api/auth.d.ts

@@ -7,7 +7,7 @@ declare namespace Api {
   namespace Auth {
     interface LoginToken {
       token: string;
-      refreshToken: string;
+      refresh_token: string;
     }
 
     interface UserInfo {

+ 1 - 1
src/typings/app.d.ts

@@ -882,7 +882,7 @@ declare namespace App {
     /** The backend service response data */
     type Response<T = unknown> = {
       access_token: string;
-      refreshToken: string;
+      refresh_token: string;
       /** The backend service response code */
       code: string;
       /** The backend service response message */

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

@@ -104,6 +104,7 @@ declare module 'vue' {
     NPopover: typeof import('naive-ui')['NPopover']
     NRadio: typeof import('naive-ui')['NRadio']
     NRadioGroup: typeof import('naive-ui')['NRadioGroup']
+    NRate: typeof import('naive-ui')['NRate']
     NScrollbar: typeof import('naive-ui')['NScrollbar']
     NSelect: typeof import('naive-ui')['NSelect']
     NSkeleton: typeof import('naive-ui')['NSkeleton']

+ 4 - 0
src/typings/elegant-router.d.ts

@@ -43,6 +43,8 @@ declare module "@elegant-router/types" {
     "delivery": "/delivery";
     "delivery_after-sales-order": "/delivery/after-sales-order";
     "delivery_normal-order": "/delivery/normal-order";
+    "delivery_split-order": "/delivery/split-order";
+    "delivery_split-order-detail": "/delivery/split-order-detail";
     "finance": "/finance";
     "finance_commodity-freight": "/finance/commodity-freight";
     "finance_summary": "/finance/summary";
@@ -201,6 +203,8 @@ declare module "@elegant-router/types" {
     | "config_order-split"
     | "delivery_after-sales-order"
     | "delivery_normal-order"
+    | "delivery_split-order-detail"
+    | "delivery_split-order"
     | "finance_commodity-freight"
     | "finance_summary"
     | "goods_desk-category"

+ 2 - 1
src/views/_builtin/login/modules/pwd-login.vue

@@ -56,7 +56,8 @@ async function handleSubmit() {
     localStg.set('userName', model.userName);
     localStg.set('password', pwd);
   }
-  await authStore.login({ ...model, sessionUUID: sessionUUID.value });
+  const res = await authStore.login({ ...model, sessionUUID: sessionUUID.value });
+  console.log(res);
 }
 async function getDataCode() {
   const uuid = getUUID();

+ 0 - 2
src/views/config/order-split/index.vue

@@ -33,8 +33,6 @@ const [registerForm, { getFieldsValue, validate, setFieldsValue }] = useForm({
       component: 'NDynamicInput',
       label: '',
       render({ model, field }) {
-        console.log(11111111, model, field);
-
         return (
           <div class={'flex flex-wrap items-center'}>
             <div class={'h38px flex items-center'}>

+ 7 - 1
src/views/delivery/after-sales-order/index.vue

@@ -457,7 +457,13 @@ async function handleExport() {
     //   { ...getFieldsValue(), returnMoneySts: activeTab.value },
     //   '售后订单列表.xlsx'
     // );
-    await fetchExportRefundOrderList({ ...getFieldsValue(), returnMoneySts: activeTab.value });
+    const newParams = getFieldsValue();
+    if (newParams.createTime) {
+      newParams.startTime = newParams.createTime[0];
+      newParams.endTime = newParams.createTime[1];
+      delete newParams.createTime;
+    }
+    await fetchExportRefundOrderList({ ...newParams, returnMoneySts: activeTab.value });
     dialog.success({
       title: '提示',
       content: () => {

+ 16 - 7
src/views/delivery/normal-order/component/normal-modal.vue

@@ -74,8 +74,9 @@ const currentSteps = computed(() => {
       return 1;
   }
 });
-function handleCopy() {
-  copyTextToClipboard(String(orderInfo.value?.orderNumber));
+function handleCopy(value: string | number | undefined) {
+  copyTextToClipboard(String(value));
+  // copyTextToClipboard(String(orderInfo.value?.orderNumber));
 }
 </script>
 
@@ -85,10 +86,18 @@ function handleCopy() {
       <div v-if="orderInfo">
         <NFlex justify="space-between" align="center">
           <NFlex>
+            <NTag v-if="orderInfo.parentOrderNumber">
+              <div class="flex items-center">
+                父订单编号: {{ orderInfo?.parentOrderNumber }}
+                <div @click="handleCopy(orderInfo?.parentOrderNumber)">
+                  <SvgIcon icon="bxs:copy" class="mx-3 cursor-pointer text-[#f97316]"></SvgIcon>
+                </div>
+              </div>
+            </NTag>
             <NTag>
               <div class="flex items-center">
-                订单编号: {{ orderInfo?.orderNumber }}
-                <div @click="handleCopy">
+                {{ orderInfo.orderLevel === 0 ? '订单编号' : '子订单编号' }}: {{ orderInfo?.orderNumber }}
+                <div @click="handleCopy(orderInfo?.orderNumber)">
                   <SvgIcon icon="bxs:copy" class="mx-3 cursor-pointer text-[#f97316]"></SvgIcon>
                 </div>
               </div>
@@ -147,7 +156,7 @@ function handleCopy() {
           <NDescriptionsItem label="配送信息">
             <div>配送方式: {{ dvyStatus[orderInfo.dvyType as keyof typeof dvyStatus] || '暂无' }}</div>
           </NDescriptionsItem>
-          <NDescriptionsItem label="付款信息">
+          <NDescriptionsItem label="父订单付款信息">
             <div>实付金额:{{ orderInfo.actualTotal }}元</div>
             <div>
               付款方式:{{
@@ -166,10 +175,10 @@ function handleCopy() {
           </NDescriptionsItem>
         </NDescriptions>
         <NDivider />
-        <NCard title="订单信息" :bordered="false">
+        <NCard title="订单信息" :bordered="false">
           <NDataTable :columns="orderColumns" :data="orderInfo.orderItems" :bordered="false" />
         </NCard>
-        <NCard title="费用信息" :bordered="false">
+        <NCard title="费用信息" :bordered="false">
           <NTable :single-line="false">
             <NThead>
               <NTr>

+ 37 - 6
src/views/delivery/normal-order/index.vue

@@ -1,5 +1,6 @@
 <script setup lang="tsx">
 import { nextTick, reactive, ref, unref, useTemplateRef, watch } from 'vue';
+import { useRouter } from 'vue-router';
 import { NImage, NTag, useDialog } from 'naive-ui';
 import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
 import {
@@ -23,6 +24,7 @@ import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { SearchForm, ShippingButton, dvyStatus, orderStatus, refundStatus } from './normal-order';
 import DeliveryModal from './component/delivery-modal.vue';
 import NormalMoadl from './component/normal-modal.vue';
+const router = useRouter();
 const appStore = useAppStore();
 const checkedRowKeys = ref([]);
 const activeTab = ref('all');
@@ -93,8 +95,12 @@ const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedT
             <div class={'mb3 flex items-center'}>
               <n-tag>
                 <div class={'flex items-center'}>
-                  订单编号:{row.orderNumber}
-                  <div onClick={() => handleCopy(row)}>
+                  {row.parentOrderNumber && <div class={''}>父订单编号:{row.parentOrderNumber}</div>}
+                  <div onClick={() => handleCopy(row, 'parentOrderNumber')}>
+                    <svgIcon icon={'bxs:copy'} class={'mx-3 cursor-pointer text-[#f97316]'}></svgIcon>
+                  </div>
+                  {row.orderLevel === 0 ? '订单编号' : '子订单编号'}:{row.orderNumber}
+                  <div onClick={() => handleCopy(row, 'orderNumber')}>
                     <svgIcon icon={'bxs:copy'} class={'mx-3 cursor-pointer text-[#f97316]'}></svgIcon>
                   </div>
                   下单时间:
@@ -216,6 +222,9 @@ const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedT
       render: row => {
         return (
           <div class={'mt7'}>
+            <n-button size="small" type="primary" ghost onClick={() => handleSplit(row)}>
+              手动拆单
+            </n-button>
             <n-button size="small" type="primary" ghost onClick={() => handleOpenMoadl(row)}>
               查看订单
             </n-button>
@@ -354,6 +363,18 @@ async function handleDeivery(row: Api.delivery.deliveryOrder) {
   }
   ShipmentModal.value?.handleOpenOrder(row.orderNumber);
 }
+function handleSplit(row: Api.delivery.deliveryOrder) {
+  if (!row.orderNumber) {
+    window.$message?.error('订单异常');
+    return;
+  }
+  router.push({
+    name: 'delivery_split-order-detail',
+    query: {
+      orderNumber: row.orderNumber
+    }
+  });
+}
 function handleOpenMoadl(row: Api.delivery.deliveryOrder) {
   if (!row.orderNumber) {
     window.$message?.error('订单异常');
@@ -381,6 +402,10 @@ async function getNums() {
       label: '待支付',
       value: 'paddingPay'
     },
+    {
+      label: '待拆单',
+      value: 'split'
+    },
     {
       label: '待发货',
       value: 'paddingShipped'
@@ -438,12 +463,12 @@ function handleReset() {
   getData();
   getNums();
 }
-async function handleCopy(row: Api.delivery.deliveryOrder) {
-  if (!row.orderNumber) {
+async function handleCopy(row: Api.delivery.deliveryOrder, key: string) {
+  if (!row[key]) {
     window.$message?.error('订单编号不存在');
     return;
   }
-  await copyTextToClipboard(row.orderNumber);
+  await copyTextToClipboard(row[key]);
 }
 function handleRefsh() {
   getData();
@@ -457,7 +482,13 @@ async function handleExport() {
     //   { ...getFieldsValue(), orderStatus: activeTab.value },
     //   '正常订单列表.xlsx'
     // );
-    await fetchExportOrderList({ ...getFieldsValue(), orderStatus: activeTab.value });
+    const newParams = getFieldsValue();
+    if (newParams.createTime) {
+      newParams.startTime = newParams.createTime[0];
+      newParams.endTime = newParams.createTime[1];
+      delete newParams.createTime;
+    }
+    await fetchExportOrderList({ ...newParams, orderStatus: activeTab.value });
     dialog.success({
       title: '提示',
       content: () => {

+ 6 - 1
src/views/delivery/normal-order/normal-order.ts

@@ -39,7 +39,12 @@ export const SearchForm: FormSchema[] = [
     }
   },
   {
-    label: '订单编号',
+    label: '父订单编号',
+    component: 'NInput',
+    field: 'parentOrderNumber'
+  },
+  {
+    label: '子订单编号',
     component: 'NInput',
     field: 'orderNumber'
   },

+ 362 - 0
src/views/delivery/split-order-detail/index.vue

@@ -0,0 +1,362 @@
+// index.vue
+
+<script setup lang="tsx">
+import { h, ref } from 'vue'; // 注意此处应该是 vue 而不是 process
+import { useRoute } from 'vue-router';
+import { NImage, NInputNumber } from 'naive-ui';
+import type { DataTableCreateSummary, DataTableRowKey } from 'naive-ui';
+import { fetchGetNomalOrderInfo } from '@/service/api/delivery/normal-orde';
+import { confirmSplit, deleteTempSubOrder, manualSplit } from '@/service/api/delivery/split-order-detail';
+import { localStg } from '@/utils/storage';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+interface OrderItem {
+  skuId: number | undefined;
+  prodCount: number;
+}
+const isSubmit = ref(false);
+
+const list = ref<{ orderItems: Api.delivery.OrderItemElement[]; subOrders: Api.delivery.OrderItemElement[] }>({
+  orderItems: [],
+  subOrders: []
+});
+const list1 = ref<Api.delivery.OrderItemElement[]>([]);
+const route = useRoute();
+const checkedRowKeysRef = ref<Api.delivery.OrderItemElement[]>([]);
+const orderItemsList = ref<OrderItem[]>([]);
+const loading = ref(true);
+
+const columns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    title: '商品',
+    key: 'goods',
+    width: 300,
+    render: row => {
+      const goodsNodes = [
+        h(NImage, { src: row.pic, width: 40, height: 40 }),
+        h('div', { class: 'ml-2 ' }, [
+          h('div', { class: 'text-12px font-semibold' }, row.skuName),
+          h('div', { class: 'text-10px text-gray' }, `规格:${row.spec || '--'}`)
+        ])
+      ];
+      return h('div', { class: 'flex items-center' }, goodsNodes);
+    }
+  },
+  {
+    title: 'sku重量',
+    key: 'weight', // 注意这里 key 应该唯一,不应重复使用 price
+    width: 100,
+    align: 'center',
+    render(row) {
+      return <div>{row.weight / 1000}kg</div>;
+    }
+  },
+  {
+    title: '购买数量',
+    key: 'prodCount',
+    width: 100,
+    align: 'center'
+  },
+  {
+    title: '已拆单数量',
+    key: 'splitCount',
+    width: 100,
+    align: 'center'
+  },
+  {
+    title: '可拆单数量',
+    key: 'availableSplitCount',
+    width: 100,
+    align: 'center',
+    render(row) {
+      const availableSplitCount = (row.prodCount ?? 0) - (row.splitCount ?? 0);
+      return <div class={availableSplitCount ? 'text-red' : ''}>{availableSplitCount}</div>;
+      // return (<div class={(row.prodCount - row.splitCount)&&'text-red'}>{row.prodCount - row.splitCount}</div>)
+    }
+  }
+];
+const columns1: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    type: 'selection'
+  },
+  {
+    title: '商品',
+    key: 'goods',
+    width: 300,
+    render: row => {
+      const goodsNodes = [
+        h(NImage, { src: row.pic, width: 40, height: 40 }),
+        h('div', { class: 'ml-2 ' }, [
+          h('div', { class: 'text-12px font-semibold' }, row.skuName),
+          h('div', { class: 'text-10px text-gray' }, `规格:${row.spec || '--'}`)
+        ])
+      ];
+      return h('div', { class: 'flex items-center' }, goodsNodes);
+    }
+  },
+  {
+    title: 'sku重量',
+    key: 'weight', // 注意这里 key 应该唯一,不应重复使用 price
+    width: 100,
+    align: 'center',
+    render(row) {
+      return <div>{row.weight / 1000}kg</div>;
+    }
+  },
+  {
+    title: '可拆单数量',
+    key: 'canSplitCount',
+    width: 100,
+    align: 'center'
+  },
+  {
+    title: '拆单数量',
+    key: 'splitCount',
+    width: 100,
+    align: 'center',
+    render(row, index) {
+      return h(NInputNumber, {
+        value: row.splitCount,
+        precision: 0,
+        min: 0,
+        max: row.canSplitCount,
+        onUpdateValue(v) {
+          list1.value[index].splitCount = v;
+          // list1.value.orderItems[index].canSplitCount -= v as number
+        }
+      });
+    }
+  }
+];
+
+const childColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    title: '商品',
+    key: 'goods',
+    width: 300,
+    render: row => {
+      const goodsNodes = [
+        h(NImage, { src: row.pic, width: 40, height: 40 }),
+        h('div', { class: 'ml-2 ' }, [
+          h('div', { class: 'text-12px font-semibold' }, row.skuName),
+          h('div', { class: 'text-10px text-gray' }, `规格:${row.spec || '--'}`)
+        ])
+      ];
+      return h('div', { class: 'flex items-center' }, goodsNodes);
+    }
+  },
+  {
+    title: 'sku重量',
+    key: 'price', // 注意这里 key 应该唯一,不应重复使用 price
+    width: 100,
+    align: 'center',
+    render(row) {
+      return <div>{row.weight / 1000}kg</div>;
+    }
+  },
+  {
+    title: '数量',
+    key: 'prodCount',
+    width: 100,
+    align: 'center'
+  }
+];
+
+const columns2: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    title: '商品',
+    key: 'goods',
+    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: 'sku重量',
+    key: 'price', // 注意这里 key 应该唯一,不应重复使用 price
+    width: 200,
+    align: 'center',
+    render(row) {
+      return <div>{row.weight / 1000}kg</div>;
+    }
+  },
+  {
+    title: '数量',
+    key: 'splitCount',
+    width: 200,
+    align: 'center'
+  }
+];
+const [registerModalPrice, { openModal, setSubLoading, closeModal }] = useModal({
+  title: '选择拆单商品',
+  width: 1200,
+  height: 700
+});
+// h(
+//   'div',
+//   {
+//     class: ' flex gap-2 items-center ' // div 的样式(内边距、边框、弹性布局)
+//   },
+//   [
+//     h(
+//       'div',
+//       { style: { color: 'red', whiteSpace: 'nowrap' } },
+//       `子订单商品的总重量:${(pageData as unknown as Api.delivery.OrderItemElement[]).reduce(
+//         (prevValue, row) => prevValue + row.weight,
+//         0
+//       ) / 1000}kg`
+//     ),
+//     'kg'
+//           ),
+//   h('div', { style: { color: 'red', whiteSpace: 'nowrap' } }, `总数量:${pageData.length}`)
+//         ]
+//       )
+const summary: DataTableCreateSummary = pageData => {
+  return {
+    splitCount: {
+      value: (
+        <div class={'flex items-center gap-2'}>
+          <div style="{ color: 'red', whiteSpace: 'nowrap' }">{`子订单商品的总重量:${
+            (pageData as unknown as Api.delivery.OrderItemElement[]).reduce(
+              (prevValue, row) => prevValue + row.weight,
+              0
+            ) / 1000
+          }kg`}</div>
+          <div style="{ color: 'red', whiteSpace: 'nowrap' }">{`总数量:${pageData.length}`}</div>
+        </div>
+      ),
+      colSpan: 1
+    }
+  };
+};
+console.log(route.query.orderNumber);
+
+async function getData() {
+  try {
+    loading.value = true;
+    const result = await fetchGetNomalOrderInfo('FD2001476085560250368');
+    // const result = await fetchGetNomalOrderInfo((route.query.orderNumber as string) || '');
+    loading.value = false;
+    if (result && result.data) {
+      const orderItems = result.data.orderItems ?? [];
+      const subOrders = result.data.subOrders ?? [];
+      list.value.orderItems = orderItems;
+      list.value.subOrders = subOrders;
+    } else {
+      console.error('接口无有效数据返回');
+    }
+  } catch (err) {
+    loading.value = false;
+    console.error('获取订单详情失败:', err);
+  }
+}
+getData();
+function handleAdd() {
+  list1.value = list.value.orderItems;
+  list1.value.forEach(i => {
+    i.canSplitCount = i.prodCount ?? 0 - i.splitCount;
+    i.splitCount = 0;
+  });
+  openModal();
+}
+// function close() {}
+async function save() {
+  isSubmit.value = true;
+  await confirmSplit({ orderNumber: route.query.orderNumber as string, splitUserId: localStg.get('lastLoginUserId') });
+  isSubmit.value = false;
+  window.$message?.success('保存成功');
+}
+function refresh() {}
+
+function handleCheck(_: DataTableRowKey[], row: Api.delivery.OrderItemElement[]) {
+  checkedRowKeysRef.value = row;
+  orderItemsList.value = checkedRowKeysRef.value.map(i => {
+    return {
+      skuId: i.skuId,
+      prodCount: i.splitCount
+    };
+  });
+  console.log('选中了', checkedRowKeysRef.value, row);
+}
+
+async function addSubmit() {
+  setSubLoading(true);
+  await manualSplit({
+    orderNumber: route.query.orderNumber as string,
+    orderItems: orderItemsList.value,
+    splitUserId: localStg.get('lastLoginUserId')
+  });
+  setSubLoading(false);
+  getData();
+  closeModal();
+}
+async function handleDel(orderNumber: string) {
+  await deleteTempSubOrder(orderNumber);
+  closeModal();
+}
+</script>
+
+<template>
+  <div>
+    <NCard title="1 父订单商品" size="medium" :bordered="false">
+      <NDataTable :columns="columns" :loading="loading" :data="list.orderItems ?? []" :bordered="false" />
+    </NCard>
+
+    <NCard title="2 拆单与子订单" size="medium" :bordered="false">
+      <NCard title="拆单" size="small" :bordered="false">
+        <NButton @click="handleAdd">选择拆单商品</NButton>
+      </NCard>
+      <NCard
+        v-for="(subOrder, index) in list.subOrders ?? []"
+        :key="index"
+        :title="'子订单(' + (index + 1) + ')'"
+        size="small"
+        :bordered="false"
+      >
+        <template #header>
+          <NButton size="small" quaternary type="primary" @click="handleDel(subOrder.orderNumber as string)">
+            删除该子订单
+          </NButton>
+        </template>
+        <NDataTable :columns="childColumns" :data="subOrder.orderItems" :bordered="false" />
+      </NCard>
+      <template #footer>
+        <NSpace justify="center">
+          <!-- <NButton  @click="close">关闭</NButton> -->
+          <NButton type="primary" :loading="isSubmit" @click="save">保存</NButton>
+        </NSpace>
+      </template>
+    </NCard>
+
+    <BasicModal @register="registerModalPrice" @after-leave="refresh" @ok="addSubmit">
+      <LayoutTable>
+        <NCard title="待拆单商品" size="small" :bordered="false">
+          <NDataTable
+            :columns="columns1"
+            :data="list1"
+            :max-height="450"
+            :row-key="row => row.orderItemId"
+            :bordered="false"
+            @update:checked-row-keys="handleCheck"
+          />
+        </NCard>
+        <NCard title="子订单商品" size="small" :bordered="false">
+          <NDataTable
+            :columns="columns2"
+            :data="checkedRowKeysRef"
+            :summary="summary"
+            :max-height="450"
+            :bordered="false"
+          />
+        </NCard>
+      </LayoutTable>
+    </BasicModal>
+  </div>
+</template>
+
+<style scoped></style>

+ 156 - 0
src/views/delivery/split-order/component/delivery-modal.vue

@@ -0,0 +1,156 @@
+<script setup lang="tsx">
+import { nextTick, ref, unref } from 'vue';
+import { fetchDeliveryOrder, fetchGetNomalOrderInfo } from '@/service/api/delivery/normal-orde';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+import { deliveryInfo } from '../split-order';
+const [registerModal, { openModal, setModalProps, setSubLoading, closeModal, setModalLoading }] = useModal({
+  title: '订单发货',
+  width: 1200,
+  height: 800,
+  isShowHeaderText: false
+});
+const [registerForm, { validate, getFieldsValue, setFieldsValue }] = useForm({
+  schemas: deliveryInfo,
+  labelWidth: 120,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+const emit = defineEmits<{
+  (e: 'finish'): void;
+}>();
+
+const orderInfo = ref<Api.delivery.deliveryOrder>();
+const isRefundIng = ref(false);
+const ShipmentColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
+  {
+    title: '商品',
+    key: 'goodsName',
+    width: 300,
+    render: row => {
+      return (
+        <div class={'flex items-center'}>
+          <n-image src={row.pic} width={80} height={80} class={'min-w-80px'}></n-image>
+          <div class={'ml2'}>
+            <div class={'text-15px font-semibold'}> {row.skuName} </div>
+            <div class={'text-gray'}>规格: {row.spec || '---'}</div>
+          </div>
+        </div>
+      );
+    }
+  },
+  {
+    title: '可发货数量',
+    key: 'num',
+    width: 250,
+    render: row => {
+      const count = Number(row.prodCount) - Number(row.refundSuccessCount);
+      if (Number(row.refundIngCount)) {
+        isRefundIng.value = true;
+      }
+      return (
+        <div class={'flex items-center justify-center'}>
+          {count} {handleCreateTag(row)}
+        </div>
+      );
+    }
+  },
+  {
+    title: '状态',
+    key: 'status',
+    align: 'center',
+    render: _row => {
+      return orderInfo.value?.hbOrderStatus == 1 ? <n-tag>等待发货</n-tag> : <n-tag type="success">已发货</n-tag>;
+    }
+  },
+  {
+    title: '发货数量',
+    key: 'count',
+    align: 'center',
+    render: row => {
+      const count = Number(row.prodCount) - Number(row.refundSuccessCount);
+      return count;
+    }
+  }
+];
+
+defineExpose({
+  setModalProps,
+  handleOpenOrder
+});
+function handleCreateTag(row: Api.delivery.OrderItemElement) {
+  if (row.refundSuccessCount) {
+    return (
+      <n-tag type="success" class={'ml2'}>
+        已扣除退款成功:{row.refundSuccessCount}
+      </n-tag>
+    );
+  }
+  if (row.refundIngCount) {
+    return (
+      <n-tag type="error" class={'ml2'}>
+        售后处理:{row.refundIngCount}
+      </n-tag>
+    );
+  }
+  return '';
+}
+async function handleOpenOrder(orderNumber: string) {
+  openModal();
+  setModalLoading(true);
+  const { data, error } = await fetchGetNomalOrderInfo(orderNumber);
+  setModalLoading(false);
+  if (!error) {
+    orderInfo.value = data;
+    const Info = {
+      ...data.userAddrOrder,
+      ...data
+    };
+    nextTick(() => {
+      setFieldsValue(Info);
+    });
+  }
+}
+async function handleSubmit() {
+  if (unref(isRefundIng)) {
+    window.$message?.error('存在售后处理中的商品,请处理后再发货');
+    return;
+  }
+  await validate();
+  setSubLoading(true);
+  const form: Recordable = {
+    ...getFieldsValue(),
+    orderNumber: orderInfo.value?.orderNumber
+  };
+  delete form.receiver;
+  delete form.mobile;
+  delete form.address;
+  const { error } = await fetchDeliveryOrder(form);
+  if (!error) {
+    emit('finish');
+    closeModal();
+  } else {
+    setSubLoading(false);
+  }
+}
+</script>
+
+<template>
+  <BasicModal @register="registerModal" @ok="handleSubmit" @after-leave="isRefundIng = false">
+    <div v-if="orderInfo">
+      <NCard :bordered="false" title="商品信息">
+        <NDataTable :scroll-x="1000" :columns="ShipmentColumns" :data="orderInfo.orderItems" :bordered="false" />
+      </NCard>
+      <NCard :bordered="false" title="订单信息">
+        <BasicForm @register-form="registerForm"></BasicForm>
+      </NCard>
+    </div>
+  </BasicModal>
+</template>
+
+<style scoped></style>

+ 186 - 0
src/views/delivery/split-order/component/normal-modal.vue

@@ -0,0 +1,186 @@
+<script setup lang="ts">
+import { ref } from 'vue';
+import dayjs from 'dayjs';
+import { fetchGetNomalOrderInfo } from '@/service/api/delivery/normal-orde';
+import { useAppStore } from '@/store/modules/app';
+import { copyTextToClipboard } from '@/utils/zt';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+import { dvyStatus, orderColumns, orderStatusEnum } from '../split-order';
+const [registerModal, { openModal, setModalLoading }] = useModal({
+  title: '订单详情',
+  isShowHeaderText: false,
+  showFooter: false,
+  width: 1200,
+  height: 800
+});
+const orderInfo = ref<Api.delivery.deliveryOrder>();
+const appStore = useAppStore();
+const TimeDown = ref<number>(0);
+const emit = defineEmits<{
+  (e: 'finish'): void;
+}>();
+async function open(orderNumber: string) {
+  openModal();
+  setModalLoading(true);
+  const { data, error } = await fetchGetNomalOrderInfo(orderNumber);
+  if (!error) {
+    orderInfo.value = data;
+    if (orderInfo.value.hbLogisticStatus == 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;
+    }
+  }
+  setModalLoading(false);
+}
+defineExpose({ open });
+function handleFinish() {
+  open(String(orderInfo.value?.orderNumber));
+  emit('finish');
+}
+
+function handleCopy(value: string | number | undefined) {
+  copyTextToClipboard(String(value));
+  // copyTextToClipboard(String(orderInfo.value?.orderNumber));
+}
+</script>
+
+<template>
+  <div>
+    <BasicModal @register="registerModal" @after-leave="orderInfo = undefined">
+      <div v-if="orderInfo">
+        <NFlex justify="space-between" align="center">
+          <NFlex>
+            <NTag v-if="orderInfo.parentOrderNumber">
+              <div class="flex items-center">
+                父订单编号: {{ orderInfo?.parentOrderNumber }}
+                <div @click="handleCopy(orderInfo?.parentOrderNumber)">
+                  <SvgIcon icon="bxs:copy" class="mx-3 cursor-pointer text-[#f97316]"></SvgIcon>
+                </div>
+              </div>
+            </NTag>
+            <NTag>
+              <div class="flex items-center">
+                {{ orderInfo.orderLevel === 0 ? '订单编号' : '子订单编号' }}: {{ orderInfo?.orderNumber }}
+                <div @click="handleCopy(orderInfo?.orderNumber)">
+                  <SvgIcon icon="bxs:copy" class="mx-3 cursor-pointer text-[#f97316]"></SvgIcon>
+                </div>
+              </div>
+            </NTag>
+            <NTag>下单时间: {{ orderInfo?.createTime }}</NTag>
+          </NFlex>
+          <NFlex vertical>
+            <template v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">
+              <div class="text-16px font-semibold">商品已拍下,等待买家付款</div>
+              <div class="text-gray">
+                如买家未在
+                <NTag :type="TimeDown > 300094 ? 'success' : 'error'">
+                  <NCountdown :duration="TimeDown" @finish="handleFinish" />
+                </NTag>
+                内付款,订单将 自动关闭。
+              </div>
+            </template>
+            <template v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_ARRIVE">
+              <div class="text-16px font-semibold">等待买家收货</div>
+              <div>商家已发货,等待确认收货</div>
+            </template>
+            <template v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_CANCEL">
+              <div class="text-16px font-semibold">已取消</div>
+              <div>取消原因:{{ orderInfo.cancelReason || '未知取消原因' }}</div>
+            </template>
+            <template v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_COMPLETE">
+              <div class="text-16px font-semibold">交易成功</div>
+              <div>买家已收货</div>
+            </template>
+          </NFlex>
+        </NFlex>
+        <NDivider />
+        <template v-if="orderInfo.hbOrderStatus != orderStatusEnum.ORDER_CANCEL">
+          <div class="p3">
+            <NSteps :current="3" :vertical="appStore.isMobile">
+              <NStep title="用户下单" :description="orderInfo.createTime" />
+              <NStep title="买家已付款" :description="orderInfo.payTime" />
+              <NStep
+                :title="orderInfo.autoSplit == 1 ? '已拆单(自动)' : `已拆单(手动-${orderInfo.splitUserName})`"
+                :description="orderInfo.dvyTime"
+              />
+            </NSteps>
+          </div>
+          <NDivider />
+        </template>
+        <NDescriptions bordered :column="appStore.isMobile ? 1 : 4">
+          <NDescriptionsItem label="收货人信息">
+            <div>收货人姓名:{{ orderInfo?.userAddrOrder?.receiver || '---' }}</div>
+            <div>收货人手机号:{{ orderInfo?.userAddrOrder?.mobile || '---' }}</div>
+            <div>收货地址:{{ orderInfo?.userAddrOrder?.address || '---' }}</div>
+          </NDescriptionsItem>
+          <NDescriptionsItem label="配送信息">
+            <div>配送方式: {{ dvyStatus[orderInfo.dvyType as keyof typeof dvyStatus] || '暂无' }}</div>
+          </NDescriptionsItem>
+          <NDescriptionsItem label="父订单付款信息">
+            <div>实付金额:{{ orderInfo.actualTotal }}元</div>
+            <div>
+              付款方式:{{
+                orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY ||
+                orderInfo.hbOrderStatus == orderStatusEnum.ORDER_CANCEL
+                  ? '暂无'
+                  : '微信'
+              }}
+            </div>
+            <div>付款时间:{{ orderInfo.payTime || '暂无' }}</div>
+          </NDescriptionsItem>
+          <NDescriptionsItem label="买家信息">
+            <div>买家昵称:{{ orderInfo?.nickName || '---' }}</div>
+            <div>买家电话:{{ orderInfo?.userMobile || '---' }}</div>
+            <div>买家留言:{{ orderInfo?.remarks || '暂无' }}</div>
+          </NDescriptionsItem>
+        </NDescriptions>
+        <NDivider />
+        <NCard title="子订单信息" :bordered="false">
+          <NDataTable :columns="orderColumns" :data="orderInfo.orderItems" :bordered="false" />
+        </NCard>
+        <NCard title="子费用信息" :bordered="false">
+          <NTable :single-line="false">
+            <NThead>
+              <NTr>
+                <NTh>费用类型</NTh>
+                <NTh>金额/元</NTh>
+              </NTr>
+            </NThead>
+            <NTbody>
+              <NTr>
+                <NTd>商品总额</NTd>
+                <NTd>{{ orderInfo.total }}</NTd>
+              </NTr>
+              <NTr>
+                <NTd>配送费(快递)</NTd>
+                <NTd>{{ orderInfo.freightAmount }}</NTd>
+              </NTr>
+              <NTr>
+                <NTd>积分</NTd>
+                <NTd>-{{ (Number(orderInfo.offsetPoints) / 100).toFixed(2) || 0 }}</NTd>
+              </NTr>
+              <NTr>
+                <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">需付款</NTd>
+                <NTd
+                  v-if="
+                    orderInfo.hbOrderStatus != orderStatusEnum.WAIT_PAY &&
+                    orderInfo.hbOrderStatus != orderStatusEnum.ORDER_CANCEL
+                  "
+                >
+                  实际付款
+                </NTd>
+                <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_CANCEL">应付款</NTd>
+                <NTd>{{ orderInfo.actualTotal }}</NTd>
+              </NTr>
+            </NTbody>
+          </NTable>
+        </NCard>
+      </div>
+    </BasicModal>
+  </div>
+</template>
+
+<style scoped></style>

+ 553 - 0
src/views/delivery/split-order/index.vue

@@ -0,0 +1,553 @@
+<script setup lang="tsx">
+import { nextTick, reactive, ref, unref, useTemplateRef, watch } from 'vue';
+import { NImage, NTag, useDialog } from 'naive-ui';
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
+import {
+  fetchBreakDownload,
+  fetchExportList,
+  fetchExportOrderList,
+  fetchGetDeliveryOrderList
+} from '@/service/api/delivery/normal-orde';
+import { fetchGetLoginUserList } from '@/service/api/common';
+import { useAppStore } from '@/store/modules/app';
+import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
+import { useAuth } from '@/hooks/business/auth';
+import { copyTextToClipboard } from '@/utils/zt';
+import { commonExport } from '@/utils/common';
+import { $t } from '@/locales';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+// import { type } from '../../../../packages/axios/src/index';
+import { SearchForm, dvyStatus } from './split-order';
+import NormalMoadl from './component/normal-modal.vue';
+const appStore = useAppStore();
+const checkedRowKeys = ref([]);
+const activeTab = ref('all');
+const orderMoadl = useTemplateRef('orderMoadl');
+const channelIdList = ref([]);
+const searchForm = ref();
+const searchParams = reactive({
+  current: 1,
+  size: 10
+});
+const [registerSearchForm, { getFieldsValue, setFieldsValue }] = useForm({
+  schemas: [
+    {
+      field: 'channelIdList',
+      label: '所属企业',
+      component: 'ApiSelect',
+      componentProps: {
+        api: () => fetchGetLoginUserList(),
+        labelFeild: 'channelName',
+        valueFeild: 'id',
+        multiple: true,
+        onUpdateValue: () => {
+          nextTick(() => {
+            handleSearch();
+          });
+        },
+        getOptions: async (options: any) => {
+          await setFieldsValue({ channelIdList: [options[0].id] });
+          handleSearch();
+        }
+      }
+    },
+    ...SearchForm
+  ],
+  showAdvancedButton: false,
+  labelWidth: 120,
+  layout: 'horizontal',
+  size: 'small',
+  gridProps: {
+    cols: '1 xl:4 s:1 l:3',
+    itemResponsive: true
+  },
+  collapsedRows: 1
+});
+const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedTable({
+  api: () =>
+    fetchGetDeliveryOrderList({
+      ...searchParams,
+      orderLevel: 0,
+      splitStatus: 4,
+      orderStatus: activeTab.value,
+      ...unref(searchForm)
+    }),
+  transform: response => defaultTransform(response),
+  immediate: false,
+  paginationProps: {
+    pageSizes: import.meta.env.VITE_PAGE_SIZE.split(',').map(Number)
+  },
+  onPaginationParamsChange: params => {
+    searchParams.current = Number(params.page);
+    searchParams.size = Number(params.pageSize);
+  },
+  columns: () => [
+    {
+      key: 'orderItems',
+      title: '商品',
+      align: 'left',
+      width: 260,
+      colSpan: (_rowData, _rowIndex) => 2,
+      render: row => {
+        return (
+          <div>
+            <div class={'mb3 flex items-center'}>
+              <n-tag>
+                <div class={'flex items-center'}>
+                  {row.parentOrderNumber && <div class={''}>父订单编号:{row.parentOrderNumber}</div>}
+                  <div onClick={() => handleCopy(row, 'parentOrderNumber')}>
+                    <svgIcon icon={'bxs:copy'} class={'mx-3 cursor-pointer text-[#f97316]'}></svgIcon>
+                  </div>
+                  {row.orderLevel === 0 ? '订单编号' : '子订单编号'}:{row.orderNumber}
+                  <div onClick={() => handleCopy(row, 'orderNumber')}>
+                    <svgIcon icon={'bxs:copy'} class={'mx-3 cursor-pointer text-[#f97316]'}></svgIcon>
+                  </div>
+                  下单时间:
+                  {row.createTime} 门店名称 {row.shopName}
+                </div>
+              </n-tag>
+            </div>
+            {row.orderItems?.map(item => {
+              return (
+                <div class={'mb-3 h-80px flex items-center'}>
+                  <NImage src={item.pic} class="h-[80px] min-w-80px w-[80px]" lazy />
+                  <div class={'ml12px flex-1'}>
+                    <div class={'w-full flex items-center justify-between'}>
+                      <div class={'w200px'}>
+                        <n-ellipsis class={'w200px'}>
+                          <span class={'w-full text-left text-15px font-semibold'}>{item.skuName}</span>
+                        </n-ellipsis>
+                      </div>
+                      <div class={'w150px pl30px text-left'}>
+                        <div class={'text-16px font-semibold'}>¥{item.price} </div>
+                      </div>
+                    </div>
+                    <div class={'w-full flex items-center justify-between'}>
+                      <div class={'w200px text-gray'}>规格: {item.spec}</div>
+                      <div class={'w150px pl30px text-left text-gray'}>x{item.prodCount} </div>
+                    </div>
+                  </div>
+                </div>
+              );
+            })}
+          </div>
+        );
+      }
+    },
+    {
+      key: 'deptName',
+      title: '单价(元)/数量',
+      align: 'center',
+      width: 100
+    },
+    {
+      key: 'actualTotal',
+      title: '实付金额(元)',
+      align: 'center',
+      width: 120,
+      render: row => {
+        return (
+          <div class={'mt7'}>
+            <div class={'text-16px text-#ff0000 font-semibold'}>{row.actualTotal} 元</div>
+            <div>共 {row.goodsTotalCount} 件</div>
+          </div>
+        );
+      }
+    },
+    {
+      key: 'payType',
+      title: '支付方式',
+      align: 'center',
+      width: 120,
+      render: row => {
+        return (
+          <NTag class={'mt7'} type="success">
+            {row.payType == 0 || row.payType == 1 ? '微信' : '未支付'}
+          </NTag>
+        );
+      }
+    },
+    {
+      key: 'seq',
+      title: '买家/收货人',
+      align: 'center',
+      width: 120,
+      render: row => {
+        return (
+          <div class={'mt7'}>
+            <div>{row.receiver}</div>
+            <div>{row.userMobile}</div>
+          </div>
+        );
+      }
+    },
+    {
+      key: 'dvyType',
+      title: '订单类型',
+      align: 'center',
+      width: 120,
+      render: row => {
+        return <NTag class={'mt7'}>{dvyStatus[row.dvyType as keyof typeof dvyStatus] || '未知类型'}</NTag>;
+      }
+    },
+    {
+      key: 'status',
+      title: '拆单方式',
+      align: 'center',
+      width: 320,
+      render: row => {
+        return (
+          <div class={'mt7'}>
+            <div>{row.autoSplit == 1 ? '自动拆单' : '手动拆单'}</div>
+            {row.autoSplit === 0 && <div>{row.splitUserName}</div>}
+          </div>
+        );
+      }
+    },
+
+    {
+      key: 'operate',
+      title: $t('common.operate'),
+      align: 'center',
+      width: 150,
+      fixed: 'right',
+      render: row => {
+        return (
+          <div class={'mt7'}>
+            <n-button size="small" type="primary" ghost onClick={() => handleOpenMoadl(row)}>
+              查看订单
+            </n-button>
+          </div>
+        );
+      }
+    }
+  ]
+});
+
+const [registerTable, { refresh, setTableLoading }] = useTable({
+  tableConfig: {
+    keyField: 'id',
+    title: '',
+    showAddButton: false,
+    showTableHeaderAction: false,
+    showSearch: false,
+    minHeight: 400
+  }
+});
+
+const exportColumns: NaiveUI.TableColumn<InternalRowData>[] = [
+  {
+    key: 'index',
+    title: '序号',
+    align: 'center',
+    width: 100,
+    render(_, rowIndex) {
+      return rowIndex + 1;
+    }
+  },
+  {
+    key: 'taskName',
+    title: '任务名称',
+    align: 'center',
+    minWidth: 100
+  },
+  {
+    key: 'updateTime',
+    title: '时间',
+    align: 'center',
+    minWidth: 100,
+    render: row => {
+      return (
+        <div>
+          <div>创建时间:{row.createTime}</div>
+          <div>完成时间:{row.updateTime}</div>
+        </div>
+      );
+    }
+  },
+  {
+    key: 'operator',
+    title: '操作人',
+    align: 'center',
+    minWidth: 100
+  },
+  {
+    key: 'exportStatus',
+    title: '状态',
+    align: 'center',
+    minWidth: 100,
+    render: row => {
+      if (row.exportStatus == 0) {
+        return (
+          <div>
+            请耐心等待,正在导出中...
+            <n-button text type="info" onClick={() => handleBreak(row.id as string)}>
+              中断
+            </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) {
+        return (
+          <div>
+            已下载&nbsp;
+            <n-button text type="info" onClick={() => handleDownload(row.id as string)}>
+              下载
+            </n-button>
+          </div>
+        );
+      } else if (row.exportStatus == 4) {
+        return <div>导出失败</div>;
+      }
+      return <div>进行中</div>;
+    }
+  }
+];
+
+async function handleDownload(id: string) {
+  setTableLoading(true);
+  await commonExport('/platform/exportTask/download', { fileId: id }, '正常订单列表.xlsx');
+  refresh();
+}
+async function handleBreak(id: string) {
+  setTableLoading(true);
+  await fetchBreakDownload(id);
+  refresh();
+}
+
+const [registerModalPrice, { openModal }] = useModal({
+  title: '导出记录',
+  width: 1200,
+  height: 600,
+  showFooter: false
+});
+
+const dialog = useDialog();
+
+function handleOpenMoadl(row: Api.delivery.deliveryOrder) {
+  if (!row.orderNumber) {
+    window.$message?.error('订单异常');
+    return;
+  }
+  orderMoadl.value?.open(String(row.orderNumber));
+}
+async function getNums() {
+  // const form = getFieldsValue();
+  // const params = {
+  //   ...form,
+  //   channelIdList: channelIdList.value,
+  //   startTime: form.createTime ? form.createTime[0] : null,
+  //   endTime: form.createTime ? form.createTime[1] : null,
+  //   createTime: null
+  // };
+  // const { data: keyData } = await fetchGetDeliveryStatusNum(params);
+  // if (!keyData) return;
+  // const orderStatusList = [
+  //   {
+  //     label: '全部',
+  //     value: 'all'
+  //   },
+  //   {
+  //     label: '待支付',
+  //     value: 'paddingPay'
+  //   },
+  //   {
+  //     label: '待拆单',
+  //     value: 'split'
+  //   },
+  //   {
+  //     label: '待发货',
+  //     value: 'paddingShipped'
+  //   },
+  //   {
+  //     label: '待收货',
+  //     value: 'paddingReceived'
+  //   },
+  //   {
+  //     label: '已完成',
+  //     value: 'completed'
+  //   },
+  //   {
+  //     label: '已取消',
+  //     value: 'cancel'
+  //   }
+  // ];
+  // const updatedOrderStatusList = orderStatusList.map(item => {
+  //   const key = item.value as keyof typeof keyData;
+  //   if (Object.hasOwn(keyData, key)) {
+  //     return {
+  //       ...item,
+  //       num: keyData[key]
+  //     };
+  //   }
+  //   return item;
+  // });
+  // // console.log(updatedOrderStatusList, 'updatedOrderStatusList');
+  // statusList.value = updatedOrderStatusList;
+}
+
+watch(
+  () => [activeTab.value],
+  () => {
+    searchParams.current = 1;
+    getData();
+  }
+);
+function handleSearch() {
+  const form = getFieldsValue();
+  if (form.createTime) {
+    form.startTime = form.createTime[0];
+    form.endTime = form.createTime[1];
+    delete form.createTime;
+  }
+  channelIdList.value = form.channelIdList;
+  searchForm.value = form;
+  getData();
+  getNums();
+}
+function handleReset() {
+  searchForm.value = getFieldsValue();
+  searchForm.value.channelIdList = channelIdList.value;
+  setFieldsValue({ channelIdList: channelIdList.value });
+  getData();
+  getNums();
+}
+async function handleCopy(row: Api.delivery.deliveryOrder, key: string) {
+  if (!row[key]) {
+    window.$message?.error('订单编号不存在');
+    return;
+  }
+  await copyTextToClipboard(row[key]);
+}
+function handleRefsh() {
+  getData();
+  getNums();
+}
+async function handleExport() {
+  loading.value = true;
+  try {
+    // await commonExport(
+    //   '/platform/order/export',
+    //   { ...getFieldsValue(), orderStatus: activeTab.value },
+    //   '正常订单列表.xlsx'
+    // );
+    const newParams = getFieldsValue();
+    if (newParams.createTime) {
+      newParams.startTime = newParams.createTime[0];
+      newParams.endTime = newParams.createTime[1];
+      delete newParams.createTime;
+    }
+    await fetchExportOrderList({ ...newParams, orderStatus: activeTab.value });
+    dialog.success({
+      title: '提示',
+      content: () => {
+        return (
+          <div>
+            <p>导出操作进行中......</p>
+            <p>是否进入导出记录</p>
+          </div>
+        );
+      },
+      positiveText: '确定',
+      negativeText: '取消',
+      onPositiveClick: () => {
+        openModal();
+      },
+      onNegativeClick: () => {}
+    });
+  } finally {
+    loading.value = false;
+  }
+}
+
+async function handleExportLog() {
+  loading.value = true;
+  try {
+    openModal();
+  } finally {
+    loading.value = false;
+  }
+}
+</script>
+
+<template>
+  <div class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <NCard :bordered="false" size="small">
+      <NCollapse display-directive="show" default-expanded-names="search">
+        <NCollapseItem title="搜索" name="search">
+          <BasicForm @register-form="registerSearchForm" @submit="handleSearch" @reset="handleReset" />
+        </NCollapseItem>
+      </NCollapse>
+    </NCard>
+    <NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+      <template #header-extra>
+        <NButton
+          v-if="useAuth().hasAuth('order:user:export')"
+          size="small"
+          type="primary"
+          class="ml20px mt30px"
+          ghost
+          :loading="loading"
+          :disabled="data.length == 0"
+          @click="handleExport"
+        >
+          <template #icon>
+            <SvgIcon icon="mingcute:file-export-line"></SvgIcon>
+          </template>
+          导出
+        </NButton>
+        <NButton
+          v-if="useAuth().hasAuth('order:user:export')"
+          size="small"
+          type="primary"
+          class="ml20px mt30px"
+          ghost
+          :loading="loading"
+          @click="handleExportLog"
+        >
+          导出记录
+        </NButton>
+      </template>
+
+      <NDataTable
+        v-model:checked-row-keys="checkedRowKeys"
+        :columns="columns"
+        :data="data"
+        size="small"
+        :flex-height="!appStore.isMobile"
+        :scroll-x="1800"
+        :loading="loading"
+        :row-key="row => row.orderId"
+        remote
+        class="sm:h-full"
+        :pagination="mobilePagination"
+      />
+      <NormalMoadl ref="orderMoadl" @finish="handleRefsh"></NormalMoadl>
+    </NCard>
+    <BasicModal @register="registerModalPrice" @after-leave="refresh">
+      <LayoutTable>
+        <ZTable
+          :show-table-action="false"
+          :columns="exportColumns"
+          :api="fetchExportList"
+          :default-params="{ exportType: 1 }"
+          @register="registerTable"
+        ></ZTable>
+      </LayoutTable>
+    </BasicModal>
+  </div>
+</template>
+
+<style scoped></style>

+ 316 - 0
src/views/delivery/split-order/split-order.ts

@@ -0,0 +1,316 @@
+import { h } from 'vue';
+import { NFlex, NImage, NTag } from 'naive-ui';
+import { fetchGetAllStoreList } from '@/service/api/goods/desk-category';
+import { fetchGetDevList } from '@/service/api/delivery/normal-orde';
+import { useAuth } from '@/hooks/business/auth';
+import type { FormSchema } from '@/components/zt/Form/types/form';
+
+export const SearchForm: FormSchema[] = [
+  {
+    label: '门店名称',
+    component: 'ApiSelect',
+    field: 'shopId',
+    componentProps: {
+      api: fetchGetAllStoreList,
+      labelFeild: 'shopName',
+      valueFeild: 'shopId'
+    }
+  },
+
+  {
+    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: '收货人姓名',
+    component: 'NInput',
+    field: 'receiver'
+  },
+  {
+    label: '收货人手机号',
+    component: 'NInput',
+    field: 'userMobile'
+  },
+  {
+    label: '售后状态',
+    component: 'NSelect',
+    field: 'refundStatus',
+    componentProps: {
+      options: [
+        {
+          label: '申请退款',
+          value: 1
+        },
+        {
+          label: '退款成功',
+          value: 2
+        },
+        {
+          label: '部分退款成功',
+          value: 3
+        },
+        {
+          label: '退款失败',
+          value: 4
+        }
+      ]
+    }
+  },
+  {
+    label: '下单时间',
+    component: 'NDatePicker',
+    field: 'createTime',
+    componentProps: {
+      type: 'datetimerange',
+      defaultTime: ['00:00:00', '23:59:59']
+    }
+  },
+  {
+    field: 'userAttrType',
+    label: '人员属性',
+    component: 'dictSelect',
+    componentProps: {
+      dictCode: 'user_attr_type',
+      immediate: true
+    },
+    show: useAuth().hasAuth('user:attr:type')
+  }
+];
+
+export enum orderStatusEnum {
+  /**
+   *
+   *  待支付
+   */
+  WAIT_PAY = 0,
+  /**
+   * 待发货
+   */
+  WAIT_DELIVERY = 1,
+  /**
+   * 订单已接单(待拣货)
+   */
+  ORDER_ACCEPT = 20,
+  /**
+   * 订单待配送(拣货完成/自提类订单为待自提)
+   */
+  ORDER_WAIT_DELIVERY = 30,
+  /**
+   * 订单配送中
+   */
+  ORDER_DELIVERY = 40,
+  /**
+   * 订单取消待审核
+   */
+  ORDER_CANCEL_WAIT_AUDIT = 50,
+  /**
+   * 订单已取消
+   */
+  ORDER_CANCEL = 60,
+  /**
+   * 订单已送达
+   */
+  ORDER_ARRIVE = 70,
+  /**
+   * 订单已完成
+   */
+  ORDER_COMPLETE = 80
+}
+/**
+ * 发货按钮显示状态
+ */
+export const ShippingButton = [
+  orderStatusEnum.WAIT_DELIVERY,
+  orderStatusEnum.ORDER_ACCEPT,
+  orderStatusEnum.ORDER_DELIVERY,
+  orderStatusEnum.ORDER_WAIT_DELIVERY
+];
+/**
+ *  // 0-待支付 1-待发货,20-订单已接单(待拣货),30-订单待配送(拣货完成/自提类订单为待自提),40-订单配送中
+        // ,50-订单取消待审核,60-订单已取消,70-订单已送达,80-订单已完成
+ */
+export const orderStatus = {
+  [orderStatusEnum.WAIT_PAY]: '待支付',
+  [orderStatusEnum.WAIT_DELIVERY]: '待发货',
+  [orderStatusEnum.ORDER_ACCEPT]: '订单已接单(待拣货)',
+  [orderStatusEnum.ORDER_WAIT_DELIVERY]: '订单待配送(拣货完成/自提类订单为待自提)',
+  [orderStatusEnum.ORDER_DELIVERY]: '订单配送中',
+  [orderStatusEnum.ORDER_CANCEL_WAIT_AUDIT]: '订单取消待审核',
+  [orderStatusEnum.ORDER_CANCEL]: '订单已取消',
+  [orderStatusEnum.ORDER_ARRIVE]: '订单已送达',
+  [orderStatusEnum.ORDER_COMPLETE]: '订单已完成'
+};
+
+/**
+ // 1:申请退款 2:退款成功 3:部分退款成功 4:退款失败
+ *
+ */
+
+export const refundStatus = {
+  1: '申请退款',
+  2: '退款成功',
+  3: '部分退款成功',
+  4: '退款失败'
+};
+/**
+ *
+ * 配送方式
+ */
+
+export const dvyStatus = {
+  1: '快递',
+  2: '自提',
+  3: '即时配送'
+};
+
+export const orderColumns: 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: 'price',
+    width: 100
+  },
+  {
+    title: '数量',
+    key: 'prodCount',
+    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}`));
+      }
+      return h(NFlex, { align: 'center' }, () => nodes);
+    }
+  },
+  {
+    title: '小计/元',
+    key: 'productTotalAmount',
+    width: 100
+  }
+];
+
+export const deliveryInfo: FormSchema[] = [
+  {
+    label: '配送方式',
+    component: 'NGradientText',
+    field: 'dvyMethod',
+    render() {
+      return h('div', {}, '快递');
+    }
+  },
+  {
+    label: '收货人姓名',
+    component: 'NGradientText',
+    field: 'receiver',
+    render({ model, field }) {
+      return h('div', {}, model[field]);
+    }
+  },
+  {
+    label: '收货人手机号',
+    component: 'NInput',
+    field: 'mobile',
+    render({ model, field }) {
+      return h('div', {}, model[field]);
+    }
+  },
+  {
+    label: '收货地址',
+    component: 'NInput',
+    field: 'address',
+
+    render({ model, field }) {
+      return h('div', {}, model[field]);
+    }
+  },
+  {
+    label: '发货方式',
+    component: 'NRadioGroup',
+    field: 'dvyType',
+    componentProps: {
+      options: [
+        {
+          label: '自己联系快递',
+          value: 1
+        },
+        {
+          label: '无需发货',
+          value: 2
+        }
+      ]
+    },
+    defaultValue: 1,
+    required: true
+  },
+  {
+    label: '快递公司',
+    component: 'ApiSelect',
+    field: 'dvyId',
+    componentProps: {
+      api: fetchGetDevList,
+      params: {
+        current: 1,
+        size: 100
+      },
+      labelFeild: 'dvyName',
+      valueFeild: 'dvyId'
+    },
+    required: true,
+    ifShow({ model }) {
+      return model.dvyType === 1;
+    }
+  },
+  {
+    label: '快递单号',
+    component: 'NInput',
+    field: 'dvyFlowId',
+    required: true,
+    ifShow({ model }) {
+      return model.dvyType === 1;
+    }
+  }
+];

+ 249 - 137
src/views/user-management/user-reviews/index.vue

@@ -1,89 +1,146 @@
 <script setup lang="tsx">
 import { NButton, NPopconfirm, NTag } from 'naive-ui';
-import { commonStatus } from '@/constants/business';
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
+import { fetchGetAllStoreList } from '@/service/api/goods/desk-category';
 import {
-  fetchAddHotSearch,
-  fetchDeleteHotSearch,
-  fetchGethotSearch,
-  fetchUpdateHotSearch
-} from '@/service/api/operation/search';
+  fetchGetBackendCommAudit,
+  fetchGetBackendCommList,
+  fetchGetReply
+} from '@/service/api/user-management/user-reviews';
+// import { useAuth } from '@/hooks/business/auth';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
-import { $t } from '@/locales';
 
-const columns: NaiveUI.TableColumn<Api.operation.HotSearch>[] = [
+const columns: NaiveUI.TableColumn<InternalRowData>[] = [
   {
-    key: 'searchName',
+    key: 'nickName',
     title: '用户昵称',
-    align: 'center'
+    align: 'center',
+    width: 200
   },
   {
-    key: 'type',
+    key: 'score',
     title: '评分',
     align: 'center',
+    width: 250,
     render: row => {
-      const arr = ['关键词', '热门搜索词', '推荐搜索词'];
-      return arr[row.type - 1] || '暂无数据';
+      return (
+        <div>
+          <div class={'flex text-[12px] text-gray'}>
+            <div class={'w-[100px] text-left'}> 商品评价:{row.score}.0 </div>
+            <n-rate class={'ml2'} size="small" readonly default-value={row.score} />
+          </div>
+          <div class={'flex text-[12px] text-gray'}>
+            <div class={'w-[100px] text-left'}> 配送打包:{row.deliveryPack}.0 </div>
+            <n-rate class={'ml2'} size="small" readonly default-value={row.deliveryPack} />
+          </div>
+          <div class={'flex text-[12px] text-gray'}>
+            <div class={'w-[100px] text-left'}> 送货速度:{row.deliverySpeed}.0 </div>
+            <n-rate class={'ml2'} size="small" readonly default-value={row.deliverySpeed} />
+          </div>
+          <div class={'flex text-[12px] text-gray'}>
+            <div class={'w-[100px] text-left'}> 配送员服务:{row.deliveryService}.0 </div>
+            <n-rate class={'ml2'} size="small" readonly default-value={row.deliveryService} />
+          </div>
+        </div>
+      );
     }
   },
   {
-    key: 'reachName1',
+    key: 'content',
     title: '评价内容',
-    align: 'center'
+    align: 'center',
+    width: 200
   },
   {
     key: 'jumpUrl',
     title: '图片',
-    align: 'center'
+    align: 'center',
+    width: 200,
+    resizable: true,
+    render: row => {
+      if (!row.imgs) return '---';
+      const imgs = (row.pics as string | undefined)?.split(',') ?? [];
+      return (
+        <div class={'flex flex-wrap'}>
+          {imgs.map((item, index) => {
+            return <n-image key={index} width="50" src={item} />;
+          })}
+        </div>
+      );
+    }
   },
   {
-    key: 'effectiveTime',
+    key: ' status',
     title: '审核状态',
-    align: 'center'
-  },
-  {
-    key: 'endTime',
-    title: '截止时间',
-    align: 'center'
-  },
-  {
-    key: 'status',
-    title: '回复内容',
     align: 'center',
     render: row => {
-      const tagMap: Record<Api.Common.commonStatus, NaiveUI.ThemeColor> = {
-        1: 'success',
-        0: 'warning'
+      const status = String(row.status); // 明确转换为字符串
+
+      const statusObject: Record<string, string> = {
+        '1': '审核通过',
+        '0': '待审核',
+        '-1': '审核不通过'
       };
-      const status = row.status || 0;
-      const label = $t(commonStatus[status]);
-      return <NTag type={tagMap[status]}>{label}</NTag>;
+
+      const tagMap: Record<string, NaiveUI.ThemeColor> = {
+        '1': 'success',
+        '0': 'warning',
+        '-1': 'error'
+      };
+
+      return <NTag type={tagMap[status]}>{statusObject[status]}</NTag>;
     }
   },
   {
-    key: 'endTime',
+    key: 'replyContent',
+    title: '回复内容',
+    align: 'center',
+    width: 200
+  },
+  {
+    key: 'userMobile',
     title: '手机号',
-    align: 'center'
+    align: 'center',
+    width: 200
   },
   {
-    key: 'endTime',
+    key: 'recTime',
     title: '提交时间',
-    align: 'center'
+    align: 'center',
+    width: 200
   },
   {
-    key: 'endTime',
+    key: 'replyTime',
     title: '回复时间',
-    align: 'center'
+    align: 'center',
+    width: 200
   },
   {
     key: 'endTime',
     title: '商品信息',
-    align: 'center'
+    align: 'center',
+    minWidth: 200,
+    maxWidth: 500,
+    resizable: true,
+    ellipsis: true,
+    render: row => {
+      const orderItems = Array.isArray(row.orderItems) ? row.orderItems : [];
+      if (!orderItems.length) return '---';
+      return (
+        <div class={'flex flex-wrap'}>
+          {orderItems.map((item, index) => {
+            return <n-image key={index} class={'mr2 shrink-0'} width="50" src={item.pic} />;
+          })}
+        </div>
+      );
+    }
   },
   {
-    key: 'endTime',
+    key: 'shopName',
     title: '门店名称',
-    align: 'center'
+    align: 'center',
+    width: 200
   }
 ];
 
@@ -91,113 +148,145 @@ const [registerTable, { refresh, setTableLoading }] = useTable({
   searchFormConfig: {
     schemas: [
       {
-        field: 'searchName',
-        label: '名称',
-        component: 'NInput'
-      }
-    ],
-    inline: false,
-    size: 'small',
-    labelPlacement: 'left',
-    isFull: false
-  },
-  tableConfig: {
-    keyField: 'id',
-    title: '用户评论列表',
-    showAddButton: true
-  }
-});
-
-async function handleDelete(row: Api.operation.HotSearch) {
-  setTableLoading(true);
-  await fetchDeleteHotSearch([row.id]);
-  refresh();
-}
-const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValue }] = useModalFrom({
-  modalConfig: {
-    title: '跳转 ',
-    width: 700,
-    isShowHeaderText: true,
-    height: 500
-  },
-  formConfig: {
-    schemas: [
-      {
-        label: '',
-        field: 'id',
-        component: 'NInput',
-        show: false
-      },
-      {
-        field: 'searchName',
-        label: '名称',
-        component: 'NInput',
-        required: true
-      },
-      {
-        field: 'type',
-        label: '类型',
+        label: '评分',
+        field: 'score',
         component: 'NSelect',
         componentProps: {
           options: [
             {
-              label: '关键词',
-              value: 1
+              label: '全部',
+              value: ''
+            },
+            {
+              label: '5分',
+              value: '5'
+            },
+            {
+              label: '4分',
+              value: '4'
             },
             {
-              label: '热门搜索词',
-              value: 2
+              label: '3分',
+              value: '3'
             },
             {
-              label: '推荐搜索词',
-              value: 3
+              label: '2分',
+              value: '2'
+            },
+            {
+              label: '1分',
+              value: '1'
             }
           ]
-        },
-        required: true
-      },
-      {
-        field: 'reachName',
-        label: '落地页名称',
-        component: 'NInput'
+        }
       },
       {
-        field: 'jumpUrl',
-        label: '跳转参数',
-        component: 'NInput'
+        label: '审核状态',
+        field: 'status',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '全部',
+              value: ''
+            },
+            {
+              label: '待审核',
+              value: '0'
+            },
+            {
+              label: '通过',
+              value: '1'
+            },
+            {
+              label: '不通过',
+              value: '-1'
+            }
+          ]
+        }
       },
       {
-        field: 'effectiveTime',
-        label: '有效时间',
-        component: 'NDatePicker',
-        required: true,
+        label: '是否回复',
+        field: 'replySts',
+        component: 'NSelect',
         componentProps: {
-          type: 'date',
-          valueFormat: 'yyyy-MM-dd'
+          options: [
+            {
+              label: '全部',
+              value: ''
+            },
+            {
+              label: '未回复',
+              value: '0'
+            },
+            {
+              label: '已回复',
+              value: '1'
+            }
+          ]
         }
       },
       {
-        field: 'endTime',
-        label: '截止时间',
+        label: '提交时间',
         component: 'NDatePicker',
-        required: true,
+        field: 'createTime',
         componentProps: {
-          type: 'date',
-          valueFormat: 'yyyy-MM-dd'
+          type: 'datetimerange',
+          defaultTime: ['00:00:00', '23:59:59']
         }
       },
       {
-        field: 'status',
-        label: '状态',
-        component: 'NSwitch',
-        required: true,
-        defaultValue: 1,
+        label: '门店名称',
+        component: 'ApiSelect',
+        field: 'shopId',
         componentProps: {
-          checkedValue: 1,
-          uncheckedValue: 0
+          api: fetchGetAllStoreList,
+          labelFeild: 'shopName',
+          valueFeild: 'shopId'
         }
       }
     ],
+    labelWidth: 120,
+    layout: 'horizontal',
+    size: 'small',
+    gridProps: {
+      cols: '1 xl:4 s:1 l:3',
+      itemResponsive: true
+    },
+    collapsedRows: 1
+  },
+  tableConfig: {
+    keyField: 'id',
+    title: '用户评论列表',
+    showAddButton: false,
+    fieldMapToTime: [['createTime', ['startTime', 'endTime']]],
+    scrollX: 3000,
+    opWdith: 200
+  }
+});
+const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValue }] = useModalFrom({
+  modalConfig: {
+    title: '回复评论',
+    width: 700,
+    isShowHeaderText: false,
+    height: 200
+  },
+  formConfig: {
+    schemas: [
+      {
+        label: '',
+        field: 'prodCommId',
+        component: 'NInput',
+        show: false
+      },
+      {
+        field: 'replyContent',
+        label: '回复内容',
+        component: 'NInput',
+        componentProps: { type: 'textarea' },
+        required: true
+      }
+    ],
     gridProps: {
       cols: '1'
     },
@@ -206,32 +295,55 @@ const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValu
 });
 async function handleSubmit() {
   const form = await getFieldsValue();
-  if (form.id) {
-    await fetchUpdateHotSearch(form);
-  } else {
-    await fetchAddHotSearch(form);
-  }
+  await fetchGetReply(form);
   closeModal();
   refresh();
 }
 
-async function edit(row: Api.operation.HotSearch) {
+async function handleAudit(row: Api.userManagement.userReviews, status: number | string) {
+  setTableLoading(true);
+  await fetchGetBackendCommAudit({ prodCommId: row.prodCommId, status });
+  refresh();
+}
+
+async function reply(row: Api.userManagement.userReviews) {
   openModal(row);
-  setFieldsValue(row);
+  await setFieldsValue({ prodCommId: row.prodCommId, replyContent: '' });
+  const form = await getFieldsValue();
+  console.log(form, row);
 }
 </script>
 
 <template>
   <LayoutTable>
-    <ZTable :columns="columns" :api="fetchGethotSearch" @register="registerTable" @add="openModal">
+    <ZTable :columns="columns" :api="fetchGetBackendCommList" @register="registerTable">
       <template #op="{ row }">
-        <NButton size="small" ghost type="primary" @click="edit(row)">编辑</NButton>
-        <NPopconfirm @positive-click="handleDelete(row)">
-          <template #trigger>
-            <NButton size="small" type="error" ghost>删除</NButton>
-          </template>
-          确定删除吗?
-        </NPopconfirm>
+        <NSpace>
+          <NButton v-if="row.status == 1 && row.replySts == 0" size="small" ghost type="primary" @click="reply(row)">
+            <template #icon>
+              <SvgIcon icon="mdi:message-reply-text"></SvgIcon>
+            </template>
+            回复
+          </NButton>
+
+          <NPopconfirm
+            v-if="row.status == 0"
+            positive-text="通过"
+            negative-text="不通过"
+            @positive-click="handleAudit(row, 1)"
+            @on-negative-click="handleAudit(row, 0)"
+          >
+            <template #trigger>
+              <NButton size="small" type="error" ghost>
+                <template #icon>
+                  <SvgIcon icon="icon-park-solid:audit"></SvgIcon>
+                </template>
+                审核
+              </NButton>
+            </template>
+            请给出审核意见
+          </NPopconfirm>
+        </NSpace>
       </template>
     </ZTable>
     <BasicModelForm @register-modal-form="registerModalForm" @submit-form="handleSubmit"></BasicModelForm>