Kaynağa Gözat

```
feat(config): 新增订单分单配置模块

- 在 `.env.test` 中更新了测试环境的后端服务地址,启用测试打包服务器路径
- 添加了订单分单管理相关接口定义及调用方法
- 增加了分单配置页面路由与国际化支持(中英文)
- 扩展了导出功能逻辑,新增导出记录查看、下载和中断功能
- 调整了售后订单和正常订单页面结构以支持新导出流程
- 修改了用户评论列表页面为热搜词管理功能,并完善其增删改查逻辑
```

wenjie 4 gün önce
ebeveyn
işleme
60a56fb9db

+ 4 - 4
.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://192.168.1.253:8114 #付
+# VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun #付
 # VITE_SERVICE_BASE_URL=http://192.168.0.157:8114 #王
-# VITE_SERVICE_BASE_URL=http://192.168.1.166: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

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

@@ -300,6 +300,7 @@ const local: App.I18n.Schema = {
     operation_search: '',
     config: '',
     'config_fright-config': '',
+    'config_order-split': '',
     delivery: '',
     'delivery_normal-order': '',
     'operation_delivery-admin': '',

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

@@ -297,6 +297,7 @@ const local: App.I18n.Schema = {
     operation_search: '',
     config: '',
     'config_fright-config': '',
+    'config_order-split': '',
     delivery: '',
     'delivery_normal-order': '',
     'operation_delivery-admin': '',

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

@@ -23,6 +23,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   about: () => import("@/views/about/index.vue"),
   config_dict: () => import("@/views/config/dict/index.vue"),
   "config_fright-config": () => import("@/views/config/fright-config/index.vue"),
+  "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"),
   "finance_commodity-freight": () => import("@/views/finance/commodity-freight/index.vue"),

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

@@ -76,6 +76,15 @@ export const generatedRoutes: GeneratedRoute[] = [
           title: 'config_fright-config',
           i18nKey: 'route.config_fright-config'
         }
+      },
+      {
+        name: 'config_order-split',
+        path: '/config/order-split',
+        component: 'view.config_order-split',
+        meta: {
+          title: 'config_order-split',
+          i18nKey: 'route.config_order-split'
+        }
       }
     ]
   },

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

@@ -185,6 +185,7 @@ const routeMap: RouteMap = {
   "config": "/config",
   "config_dict": "/config/dict",
   "config_fright-config": "/config/fright-config",
+  "config_order-split": "/config/order-split",
   "delivery": "/delivery",
   "delivery_after-sales-order": "/delivery/after-sales-order",
   "delivery_normal-order": "/delivery/normal-order",

+ 25 - 0
src/service/api/common.ts

@@ -57,6 +57,31 @@ export function fetchChannelList(data: any) {
   });
 }
 
+/**
+ * 后管端-分单管理列表
+ * @returns
+ */
+
+export function fetchOrderSplitList(data: any) {
+  return request<Api.config.orderSplit[]>({
+    url: '/platform/channel/listAllSplitRule',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 后管端-分单管理批量修改
+ */
+
+export function fetchBatchOrderSplit(splitRule: number, data: any) {
+  return request({
+    url: `/platform/channel/batch?splitRule=${splitRule}`,
+    method: 'put',
+    data
+  });
+}
+
 /**
  * 获取当前登录用户的所属企业
  * @returns

+ 13 - 0
src/service/api/delivery/after-sales-order/index.ts

@@ -13,6 +13,19 @@ export function fetchGetAfterSalesOrderList(data: any) {
   });
 }
 
+/**
+ * 导出正常快递订单
+ * @param data
+ * @returns
+ */
+export function fetchExportRefundOrderList(data: any) {
+  return request({
+    url: '/platform/orderRefund/export',
+    method: 'get',
+    params: data
+  });
+}
+
 /**
  * 导出售后快递订单
  * @param data

+ 38 - 0
src/service/api/delivery/normal-orde/index.ts

@@ -13,6 +13,44 @@ export function fetchGetDeliveryOrderList(data: any) {
   });
 }
 
+/**
+ * 导出正常快递订单
+ * @param data
+ * @returns
+ */
+export function fetchExportOrderList(data: any) {
+  return request({
+    url: '/platform/order/export',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 导出正常快递订单记录
+ * @param data
+ * @returns
+ */
+export function fetchExportList(data: any) {
+  return request({
+    url: '/platform/exportTask/page',
+    method: 'get',
+    params: data
+  });
+}
+
+/**
+ * 下载
+ * @param data
+ * @returns
+ */
+export function fetchBreakDownload(fileId: any) {
+  return request({
+    url: `/platform/exportTask/cancel/${fileId}`,
+    method: 'get'
+  });
+}
+
 /**
  *  获取快递订单状态数量
  * @returns

+ 16 - 0
src/typings/api.d.ts

@@ -1913,6 +1913,22 @@ declare namespace Api {
       [property: string]: any;
     }
   }
+  namespace config {
+    interface orderSplit {
+      autoSplit?: number;
+      channelIdList?: any[];
+      channelName?: string;
+      channelNo?: string;
+      createTime?: string;
+      id?: number;
+      splitRule?: number;
+      splitWeight?: number;
+      status?: number;
+      type?: number;
+      userAttrType?: string;
+      [property: string]: any;
+    }
+  }
   namespace System {
     /** 字典类型 */
     type DictType = Common.CommonRecord<{

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

@@ -39,6 +39,7 @@ declare module "@elegant-router/types" {
     "config": "/config";
     "config_dict": "/config/dict";
     "config_fright-config": "/config/fright-config";
+    "config_order-split": "/config/order-split";
     "delivery": "/delivery";
     "delivery_after-sales-order": "/delivery/after-sales-order";
     "delivery_normal-order": "/delivery/normal-order";
@@ -197,6 +198,7 @@ declare module "@elegant-router/types" {
     | "about"
     | "config_dict"
     | "config_fright-config"
+    | "config_order-split"
     | "delivery_after-sales-order"
     | "delivery_normal-order"
     | "finance_commodity-freight"

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

@@ -0,0 +1,211 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDynamicInput, NInputNumber, NSelect } from 'naive-ui';
+import { fetchGetAllChannelList } from '@/service/api/goods/store-goods';
+import {
+  // fetchChannelList,
+  fetchBatchOrderSplit,
+  fetchOrderSplitList
+} from '@/service/api/common';
+import { useTabStore } from '@/store/modules/tab';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+const isSubmit = ref(false);
+const tabStore = useTabStore();
+interface Options {
+  value: any;
+  index: number;
+}
+const ChannelOptions = ref<Api.goods.Channel[]>([]);
+const openOptions = ref([
+  { label: '是', value: 1 },
+  { label: '否', value: 0 }
+]);
+const [registerForm, { getFieldsValue, validate, setFieldsValue }] = useForm({
+  schemas: [
+    {
+      field: 'transportId',
+      component: 'NInput',
+      label: 'false',
+      show: false
+    },
+    {
+      field: 'name',
+      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'}>
+              <div class={'ml-3 w-200px text-center'}> 是否启用</div>
+              <div class={'w-200px text-center'}> 企业 </div>
+              <div class={'ml-3 w-200px text-center'}> 重量(kg) </div>
+            </div>
+            <NDynamicInput
+              value={model[field]}
+              onUpdate:value={value => (model[field] = value)}
+              v-slots={(row: Options) => handleCreatInput(model, field, row)}
+              onCreate={() => handleAdd()}
+              max={ChannelOptions.value.length}
+              onRemove={handleRemove}
+            ></NDynamicInput>
+          </div>
+        );
+      },
+      required: true,
+      defaultValue: []
+    }
+  ],
+  labelWidth: 120,
+  layout: 'horizontal',
+  collapsedRows: 1,
+  showActionButtonGroup: false,
+  gridProps: {
+    cols: '1',
+    itemResponsive: true
+  }
+});
+function handleCreatInput(model: any, field: any, row: Options) {
+  return (
+    <div class={'flex items-center'}>
+      <div class={'w-200px'}>
+        <NSelect
+          value={model[field][row.index].autoSplit}
+          options={openOptions.value}
+          onUpdate:value={vlaue => {
+            model[field][row.index].autoSplit = vlaue ? Number(vlaue) : undefined;
+          }}
+        ></NSelect>
+      </div>
+      <div class={'ml-3 w-200px'}>
+        <NSelect
+          value={model[field][row.index].id}
+          labelField="channelName"
+          valueField="id"
+          options={ChannelOptions.value}
+          onUpdate:value={vlaue => {
+            ChannelOptions.value.map(it => {
+              if (it.id == model[field][row.index].id) {
+                it.disabled = false;
+              }
+              return it;
+            });
+            model[field][row.index].id = vlaue ? Number(vlaue) : undefined;
+          }}
+          onUpdate:show={value => {
+            ChannelOptions.value.map(it => {
+              if (it.id == model[field][row.index].id) {
+                if (value) {
+                  it.disabled = false;
+                }
+                if (!value) {
+                  it.disabled = true;
+                }
+              }
+              return it;
+            });
+          }}
+        ></NSelect>
+      </div>
+      <div class={'ml-3 w-200px'}>
+        <NInputNumber
+          value={model[field][row.index].splitWeight}
+          min={1}
+          onUpdate:value={value => {
+            model[field][row.index].splitWeight = value;
+          }}
+          v-slots={{ prefix: () => '每', suffix: () => 'Kg' }}
+        ></NInputNumber>
+      </div>
+    </div>
+  );
+}
+function close() {
+  tabStore.removeTab(tabStore.activeTabId);
+}
+
+async function getChannelList() {
+  const { data } = await fetchGetAllChannelList();
+  if (!data) return;
+  ChannelOptions.value = data;
+}
+getChannelList();
+
+async function getData() {
+  const { data } = await fetchOrderSplitList({ splitRule: 1 });
+  if (data) {
+    setFieldsValue({
+      name: data.map(it => {
+        return {
+          id: it.id,
+          autoSplit: it.autoSplit,
+          splitWeight: it.splitWeight,
+          splitRule: it.splitRule
+        };
+      })
+    });
+    const dataKey = data.map(it => it.channelId);
+    ChannelOptions.value.map(it => {
+      if (dataKey.includes(Number(it.id))) {
+        it.disabled = true;
+      }
+      return it;
+    });
+  }
+}
+getData();
+function handleAdd() {
+  return {
+    id: null,
+    autoSplit: null,
+    splitWeight: null,
+    splitRule: 1
+  };
+}
+
+async function save() {
+  await validate();
+  isSubmit.value = true;
+  const form = getFieldsValue();
+  console.log(form);
+
+  await fetchBatchOrderSplit(1, form.name);
+  isSubmit.value = false;
+  window.$message?.success('保存成功');
+  getData();
+}
+function handleRemove() {
+  const form = getFieldsValue();
+  return console.log(form);
+}
+// async function handleGetChannelId(channelId: number, idx: number) {
+//   const res = await fetchGetTransport(channelId);
+//   console.log(res);
+
+//   const form = getFieldsValue();
+//   const newName = JSON.parse(JSON.stringify(form.name));
+//   newName[idx].distance = res.data?.distance || null;
+//   newName[idx].freightFee = res.data?.freightFee || null;
+//   newName[idx].weight = res.data?.weight || null;
+//   setFieldsValue({ name: newName });
+// }
+</script>
+
+<template>
+  <LayoutTable>
+    <NCard title="分单配置" :bordered="false" size="small" segmented class="card-wrapper">
+      <NScrollbar x-scrollable>
+        <BasicForm @register-form="registerForm"></BasicForm>
+      </NScrollbar>
+      <template #footer>
+        <NSpace justify="end">
+          <NButton size="small" @click="close">关闭</NButton>
+          <NButton type="primary" size="small" :loading="isSubmit" @click="save">保存</NButton>
+        </NSpace>
+      </template>
+    </NCard>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

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

@@ -1,13 +1,21 @@
 <script setup lang="tsx">
 import { nextTick, reactive, ref, unref, useTemplateRef, watch } from 'vue';
-import { NImage, NTag } from 'naive-ui';
-import { fetchGetAfterSalesOrderList, fetchGetAfterSalesStatusNum } from '@/service/api/delivery/after-sales-order';
+import { NImage, NTag, useDialog } from 'naive-ui';
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
+import {
+  fetchExportRefundOrderList,
+  fetchGetAfterSalesOrderList,
+  fetchGetAfterSalesStatusNum
+} from '@/service/api/delivery/after-sales-order';
+import { fetchBreakDownload, fetchExportList } 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 { useModal } from '@/components/zt/Modal/hooks/useModal';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { $t } from '@/locales';
 import { useForm } from '@/components/zt/Form/hooks/useForm';
 import NormalMoadl from '../normal-order/component/normal-modal.vue';
@@ -249,6 +257,105 @@ const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedT
     }
   ]
 });
+
+const [registerModalPrice, { openModal }] = useModal({
+  title: '导出记录',
+  width: 1200,
+  height: 600,
+  showFooter: false
+});
+
+const dialog = useDialog();
+
+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>;
+    }
+  }
+];
 function handleOpenMoadl(row: Api.delivery.OrderRefund) {
   if (!row.orderNumber) {
     window.$message?.error('订单异常');
@@ -341,18 +448,55 @@ function handleReset() {
   getData();
   getNums();
 }
+
 async function handleExport() {
   loading.value = true;
   try {
-    await commonExport(
-      '/platform/orderRefund/export',
-      { ...getFieldsValue(), returnMoneySts: activeTab.value },
-      '售后订单列表.xlsx'
-    );
+    // await commonExport(
+    //   '/platform/orderRefund/export',
+    //   { ...getFieldsValue(), returnMoneySts: activeTab.value },
+    //   '售后订单列表.xlsx'
+    // );
+    await fetchExportRefundOrderList({ ...getFieldsValue(), returnMoneySts: 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;
+  }
+}
+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();
+}
 </script>
 
 <template>
@@ -398,6 +542,17 @@ async function handleExport() {
           </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"
@@ -416,6 +571,18 @@ async function handleExport() {
       <NormalMoadl ref="orderMoadl" @finish="handleReset"></NormalMoadl>
       <OrderModal ref="AfterSalesModal"></OrderModal>
     </NCard>
+
+    <BasicModal @register="registerModalPrice" @after-leave="refresh">
+      <LayoutTable>
+        <ZTable
+          :show-table-action="false"
+          :columns="exportColumns"
+          :api="fetchExportList"
+          :default-params="{ exportType: 2 }"
+          @register="registerTable"
+        ></ZTable>
+      </LayoutTable>
+    </BasicModal>
   </div>
 </template>
 

+ 176 - 7
src/views/delivery/normal-order/index.vue

@@ -1,7 +1,14 @@
 <script setup lang="tsx">
 import { nextTick, reactive, ref, unref, useTemplateRef, watch } from 'vue';
-import { NImage, NTag } from 'naive-ui';
-import { fetchGetDeliveryOrderList, fetchGetDeliveryStatusNum } from '@/service/api/delivery/normal-orde';
+import { NImage, NTag, useDialog } from 'naive-ui';
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
+import {
+  fetchBreakDownload,
+  fetchExportList,
+  fetchExportOrderList,
+  fetchGetDeliveryOrderList,
+  fetchGetDeliveryStatusNum
+} 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';
@@ -10,6 +17,9 @@ 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, ShippingButton, dvyStatus, orderStatus, refundStatus } from './normal-order';
 import DeliveryModal from './component/delivery-modal.vue';
 import NormalMoadl from './component/normal-modal.vue';
@@ -227,6 +237,116 @@ const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedT
   ]
 });
 
+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();
+
 async function handleDeivery(row: Api.delivery.deliveryOrder) {
   if (!row.orderNumber) {
     window.$message?.error('订单异常');
@@ -332,11 +452,38 @@ function handleRefsh() {
 async function handleExport() {
   loading.value = true;
   try {
-    await commonExport(
-      '/platform/order/export',
-      { ...getFieldsValue(), orderStatus: activeTab.value },
-      '正常订单列表.xlsx'
-    );
+    // await commonExport(
+    //   '/platform/order/export',
+    //   { ...getFieldsValue(), orderStatus: activeTab.value },
+    //   '正常订单列表.xlsx'
+    // );
+    await fetchExportOrderList({ ...getFieldsValue(), 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;
   }
@@ -386,6 +533,17 @@ async function handleExport() {
           </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
@@ -404,6 +562,17 @@ async function handleExport() {
       <NormalMoadl ref="orderMoadl" @finish="handleRefsh"></NormalMoadl>
       <DeliveryModal ref="Shipment" @finish="handleRefsh"></DeliveryModal>
     </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>
 

+ 195 - 21
src/views/user-management/user-reviews/index.vue

@@ -1,47 +1,98 @@
 <script setup lang="tsx">
-import { NImage } from 'naive-ui';
-import { fetchGetUserList } from '@/service/api/user-management/user-list';
+import { NButton, NPopconfirm, NTag } from 'naive-ui';
+import { commonStatus } from '@/constants/business';
+import {
+  fetchAddHotSearch,
+  fetchDeleteHotSearch,
+  fetchGethotSearch,
+  fetchUpdateHotSearch
+} from '@/service/api/operation/search';
 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.userManagement.userList>[] = [
+const columns: NaiveUI.TableColumn<Api.operation.HotSearch>[] = [
   {
-    key: 'nickName',
+    key: 'searchName',
     title: '用户昵称',
     align: 'center'
   },
   {
-    key: 'pic',
-    title: '头像1',
+    key: 'type',
+    title: '评分',
     align: 'center',
-    render: row => <NImage src={row.pic} class="h-[80px] min-w-80px w-[80px]" lazy />
+    render: row => {
+      const arr = ['关键词', '热门搜索词', '推荐搜索词'];
+      return arr[row.type - 1] || '暂无数据';
+    }
+  },
+  {
+    key: 'reachName1',
+    title: '评价内容',
+    align: 'center'
+  },
+  {
+    key: 'jumpUrl',
+    title: '图片',
+    align: 'center'
+  },
+  {
+    key: 'effectiveTime',
+    title: '审核状态',
+    align: 'center'
   },
   {
-    key: 'userMobile',
-    title: '手机号码',
+    key: 'endTime',
+    title: '截止时间',
     align: 'center'
   },
   {
-    key: 'name',
-    title: '渠道',
+    key: 'status',
+    title: '回复内容',
     align: 'center',
     render: row => {
-      const arr = ['企业用户', 'B端用户', '市民请集合'];
-      return arr[row.platform - 1] || '--';
+      const tagMap: Record<Api.Common.commonStatus, NaiveUI.ThemeColor> = {
+        1: 'success',
+        0: 'warning'
+      };
+      const status = row.status || 0;
+      const label = $t(commonStatus[status]);
+      return <NTag type={tagMap[status]}>{label}</NTag>;
     }
   },
   {
-    key: 'userRegtime',
-    title: '创建时间',
+    key: 'endTime',
+    title: '手机号',
+    align: 'center'
+  },
+  {
+    key: 'endTime',
+    title: '提交时间',
+    align: 'center'
+  },
+  {
+    key: 'endTime',
+    title: '回复时间',
+    align: 'center'
+  },
+  {
+    key: 'endTime',
+    title: '商品信息',
+    align: 'center'
+  },
+  {
+    key: 'endTime',
+    title: '门店名称',
     align: 'center'
   }
 ];
 
-const [registerTable] = useTable({
+const [registerTable, { refresh, setTableLoading }] = useTable({
   searchFormConfig: {
     schemas: [
       {
-        field: 'nickName',
-        label: '用户昵称',
+        field: 'searchName',
+        label: '称',
         component: 'NInput'
       }
     ],
@@ -52,15 +103,138 @@ const [registerTable] = useTable({
   },
   tableConfig: {
     keyField: 'id',
-    title: '用户列表',
-    showAddButton: false
+    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: '类型',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '关键词',
+              value: 1
+            },
+            {
+              label: '热门搜索词',
+              value: 2
+            },
+            {
+              label: '推荐搜索词',
+              value: 3
+            }
+          ]
+        },
+        required: true
+      },
+      {
+        field: 'reachName',
+        label: '落地页名称',
+        component: 'NInput'
+      },
+      {
+        field: 'jumpUrl',
+        label: '跳转参数',
+        component: 'NInput'
+      },
+      {
+        field: 'effectiveTime',
+        label: '有效时间',
+        component: 'NDatePicker',
+        required: true,
+        componentProps: {
+          type: 'date',
+          valueFormat: 'yyyy-MM-dd'
+        }
+      },
+      {
+        field: 'endTime',
+        label: '截止时间',
+        component: 'NDatePicker',
+        required: true,
+        componentProps: {
+          type: 'date',
+          valueFormat: 'yyyy-MM-dd'
+        }
+      },
+      {
+        field: 'status',
+        label: '状态',
+        component: 'NSwitch',
+        required: true,
+        defaultValue: 1,
+        componentProps: {
+          checkedValue: 1,
+          uncheckedValue: 0
+        }
+      }
+    ],
+    gridProps: {
+      cols: '1'
+    },
+    labelWidth: 120
+  }
+});
+async function handleSubmit() {
+  const form = await getFieldsValue();
+  if (form.id) {
+    await fetchUpdateHotSearch(form);
+  } else {
+    await fetchAddHotSearch(form);
+  }
+  closeModal();
+  refresh();
+}
+
+async function edit(row: Api.operation.HotSearch) {
+  openModal(row);
+  setFieldsValue(row);
+}
 </script>
 
 <template>
   <LayoutTable>
-    <ZTable :columns="columns" :api="fetchGetUserList" :show-table-action="false" @register="registerTable"></ZTable>
+    <ZTable :columns="columns" :api="fetchGethotSearch" @register="registerTable" @add="openModal">
+      <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>
+      </template>
+    </ZTable>
+    <BasicModelForm @register-modal-form="registerModalForm" @submit-form="handleSubmit"></BasicModelForm>
   </LayoutTable>
 </template>