Browse Source

```
feat(config): 新增配送费与订单拆分配置模块

- 在 .env.test 中调整了测试环境的后端服务地址注释顺序
- basic-form.vue 中增强了表单校验逻辑,支持数组字段的完整性校验
- 表单类型定义中新增 NDynamicInput 组件支持
- z-table.vue 和 useTable.ts 中增加了 getSeachForm 方法用于获取搜索表单数据
- 国际化文件 en-us.ts 与 zh-cn.ts 中添加了配置相关路由的翻译字段
- 路由配置 imports.ts、routes.ts、transform.ts 中注册了新的配置页面路由
- 商品接口中新增导出商品功能 fetchExportAllGoods
- 搜索管理接口路径及参数结构调整,适配后端接口变更
- 类型定义文件中更新了 operation 模块 HotSearch 接口字段结构
- 新增两个配置页面:配送

zhangtao 5 days ago
parent
commit
def60ce578

+ 3 - 2
.env.test

@@ -1,8 +1,9 @@
 # backend service base url, test environment
 # VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun
-# VITE_SERVICE_BASE_URL=http://192.168.1.206:8114 #付
-VITE_SERVICE_BASE_URL=http://192.168.0.157:8114 #王
+VITE_SERVICE_BASE_URL=http://192.168.1.206:8114 #付
+# VITE_SERVICE_BASE_URL=http://192.168.0.157:8114 #王
 # VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
+# VITE_SERVICE_BASE_URL=https://plat.qlapp.cn #服务器
 
 
 # other backend service base url, test environment

+ 17 - 10
src/components/zt/Form/basic-form.vue

@@ -4,7 +4,7 @@ import type { Ref } from 'vue';
 import type { ButtonProps, FormItemRule } from 'naive-ui';
 import { DownOutlined, QuestionCircleOutlined, UpOutlined } from '@vicons/antd';
 import type { GridProps } from 'naive-ui/lib/grid';
-import { deepMerge } from '@/utils/zt';
+import { areAllItemsAllFieldsFilled, deepMerge } from '@/utils/zt';
 import { isBoolean, isFunction } from '@/utils/zt/is';
 import { createPlaceholderMessage } from './helper';
 import { useFormEvents } from './hooks/useFormEvents';
@@ -119,21 +119,28 @@ export default defineComponent({
 
       return schemas as FormSchema[];
     });
-    const getRule = (schema: FormSchema): FormItemRule | undefined => {
+    function getRule(schema: FormSchema): FormItemRule | FormItemRule[] | undefined {
       if (schema.required && !schema.rules) {
         return {
           required: true,
           message: `${schema.label}不能为空`,
           trigger: ['blur', 'input', 'change'],
           validator: () => {
-            if (
-              formModel[schema.field] === undefined ||
-              formModel[schema.field] === null ||
-              formModel[schema.field] === ''
-            ) {
-              return new Error(`${schema.label}不能为空`);
+            const value = formModel[schema.field];
+            // 数组校验
+            if (Array.isArray(value)) {
+              if (value.length === 0) {
+                return new Error(`${schema.label}不能为空`);
+              }
+              if (!areAllItemsAllFieldsFilled(value)) {
+                return new Error(`${schema.label}中存在空项,请完善所有项目`);
+              }
+
+              return true;
             }
-            if (Array.isArray(formModel[schema.field]) && formModel[schema.field].length === 0) {
+
+            // 非数组的原有校验逻辑
+            if (value === undefined || value === null || value === '') {
               return new Error(`${schema.label}不能为空`);
             }
             return true;
@@ -141,7 +148,7 @@ export default defineComponent({
         };
       }
       return undefined;
-    };
+    }
     function getShow(schema: FormSchema) {
       if (isBoolean(schema.show)) {
         return schema.show;

+ 6 - 2
src/components/zt/Form/types/form.ts

@@ -6,6 +6,7 @@ import {
   type CheckboxGroupProps,
   type CheckboxProps,
   type DatePickerProps,
+  type DynamicInputProps,
   type DynamicTagsProps,
   type FormItemRule,
   type InputNumberProps,
@@ -29,6 +30,7 @@ import {
   NCheckbox,
   NCheckboxGroup,
   NDatePicker,
+  NDynamicInput,
   NDynamicTags,
   NInput,
   NInputNumber,
@@ -127,7 +129,8 @@ export type FormSchema =
   | FormSchemaWithType<'NDynamicTags', DynamicTagsProps>
   | FormSchemaWithType<'ApiSelect', ApiSelectProps>
   | FormSchemaWithType<'ApiTreeSelect', ApiTreeSelectProps>
-  | FormSchemaWithType<'zUpload', zuploadProps>;
+  | FormSchemaWithType<'zUpload', zuploadProps>
+  | FormSchemaWithType<'NDynamicInput', DynamicInputProps>;
 
 export interface RenderCallbackParams {
   schema: FormSchema;
@@ -209,7 +212,8 @@ export const componentMap = {
   NMention,
   ApiSelect,
   ApiTreeSelect,
-  zUpload
+  zUpload,
+  NDynamicInput
 } as const;
 
 export type ComponentMap = typeof componentMap;

+ 4 - 0
src/components/zt/Table/hooks/useTable.ts

@@ -44,6 +44,9 @@ export function useTable(tableProps: ztTableProps): UseTableReturnType {
     },
     getTableData() {
       return tableRef.value?.getTableData() as any[];
+    },
+    getSeachForm() {
+      return tableRef.value?.getSeachForm() as Recordable;
     }
   };
   return [register, methods];
@@ -56,6 +59,7 @@ export interface TableMethods {
   setTableConfig: (config: tableProp) => void;
   getTableCheckedRowKeys: () => string[];
   getTableData: () => any[];
+  getSeachForm: () => Recordable;
 }
 
 export type RegisterFn = (TableInstance: TableMethods) => void;

+ 2 - 1
src/components/zt/Table/z-table.vue

@@ -91,7 +91,8 @@ export default defineComponent({
       setTableLoading,
       setTableConfig,
       getTableCheckedRowKeys,
-      getTableData
+      getTableData,
+      getSeachForm
     };
     function setTableLoading(flage: boolean) {
       loading.value = flage;

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

@@ -296,7 +296,10 @@ const local: App.I18n.Schema = {
     'user-management_user-list': '',
     operation: '',
     operation_advertisement: '',
-    operation_search: ''
+    operation_search: '',
+    config: '',
+    'config_fright-config': '',
+    'config_order-splitting': ''
   },
   page: {
     login: {

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

@@ -293,7 +293,10 @@ const local: App.I18n.Schema = {
     'user-management_user-list': '',
     operation: '',
     operation_advertisement: '',
-    operation_search: ''
+    operation_search: '',
+    config: '',
+    'config_fright-config': '',
+    'config_order-splitting': ''
   },
   page: {
     login: {

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

@@ -21,6 +21,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   "iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
   login: () => import("@/views/_builtin/login/index.vue"),
   about: () => import("@/views/about/index.vue"),
+  "config_fright-config": () => import("@/views/config/fright-config/index.vue"),
+  "config_order-splitting": () => import("@/views/config/order-splitting/index.vue"),
   "goods_desk-category": () => import("@/views/goods/desk-category/index.vue"),
   "goods_store-goods": () => import("@/views/goods/store-goods/index.vue"),
   goods_tag: () => import("@/views/goods/tag/index.vue"),

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

@@ -50,6 +50,35 @@ export const generatedRoutes: GeneratedRoute[] = [
       order: 10
     }
   },
+  {
+    name: 'config',
+    path: '/config',
+    component: 'layout.base',
+    meta: {
+      title: 'config',
+      i18nKey: 'route.config'
+    },
+    children: [
+      {
+        name: 'config_fright-config',
+        path: '/config/fright-config',
+        component: 'view.config_fright-config',
+        meta: {
+          title: 'config_fright-config',
+          i18nKey: 'route.config_fright-config'
+        }
+      },
+      {
+        name: 'config_order-splitting',
+        path: '/config/order-splitting',
+        component: 'view.config_order-splitting',
+        meta: {
+          title: 'config_order-splitting',
+          i18nKey: 'route.config_order-splitting'
+        }
+      }
+    ]
+  },
   {
     name: 'goods',
     path: '/goods',

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

@@ -182,6 +182,9 @@ const routeMap: RouteMap = {
   "404": "/404",
   "500": "/500",
   "about": "/about",
+  "config": "/config",
+  "config_fright-config": "/config/fright-config",
+  "config_order-splitting": "/config/order-splitting",
   "goods": "/goods",
   "goods_desk-category": "/goods/desk-category",
   "goods_store-goods": "/goods/store-goods",

+ 13 - 0
src/service/api/goods/store-goods/index.ts

@@ -51,3 +51,16 @@ export function fetchGetAllChannelList() {
     method: 'get'
   });
 }
+
+/**
+ * 导出商品
+ * @param params
+ * @returns
+ */
+export function fetchExportAllGoods(params: any) {
+  return request({
+    url: '/platform/channelProd/export',
+    method: 'get',
+    params
+  });
+}

+ 5 - 5
src/service/api/operation/search/index.ts

@@ -7,7 +7,7 @@ import { request } from '@/service/request';
  */
 export function fetchGethotSearch(params: any) {
   return request<Api.operation.HotSearch[]>({
-    url: '/platform/hotSearch/page',
+    url: '/platform/SearchTerm/page',
     method: 'get',
     params
   });
@@ -20,7 +20,7 @@ export function fetchGethotSearch(params: any) {
 
 export function fetchAddHotSearch(params: any) {
   return request({
-    url: '/platform/hotSearch',
+    url: '/platform/SearchTerm',
     method: 'post',
     data: params
   });
@@ -32,9 +32,9 @@ export function fetchAddHotSearch(params: any) {
 */
 export function fetchDeleteHotSearch(params: any) {
   return request({
-    url: '/platform/hotSearch',
+    url: '/platform/SearchTerm',
     method: 'delete',
-    params
+    data: params
   });
 }
 
@@ -46,7 +46,7 @@ export function fetchDeleteHotSearch(params: any) {
  */
 export function fetchUpdateHotSearch(params: any) {
   return request({
-    url: '/platform/hotSearch',
+    url: '/platform/SearchTerm',
     method: 'put',
     data: params
   });

+ 22 - 14
src/typings/api.d.ts

@@ -749,33 +749,41 @@ declare namespace Api {
   namespace operation {
     interface HotSearch {
       /**
-       * 内容
+       * 	创建时间
        */
-      content?: string;
+      createTime: string;
       /**
-       * 主键
+       * 	有效时间
        */
-      hotSearchId?: number;
+      effectiveTime: string;
       /**
-       * 录入时间
+       *  结束时间
        */
-      recDate?: string;
+      endTime: string;
       /**
-       * 顺序
+       *  id
        */
-      seq?: number;
+      id: number;
       /**
-       * 店铺id
+       * 	跳转参数
        */
-      shopId?: number;
+      jumpUrl: string;
       /**
-       * 状态 默认是1,0为下线
+       * 	落地页名称
        */
-      status?: number;
+      reachName: string;
+      /**
+       * 	搜索名称
+       */
+      searchName: string;
+      /**
+       * 	状态 1正常 0关闭
+       */
+      status: Common.commonStatus;
       /**
-       * 标题
+       * 类型 1-关键词 2-热门搜索词 3-推荐搜索词
        */
-      title?: string;
+      type: number;
       [property: string]: any;
     }
     /**

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

@@ -76,6 +76,7 @@ declare module 'vue' {
     NDrawer: typeof import('naive-ui')['NDrawer']
     NDrawerContent: typeof import('naive-ui')['NDrawerContent']
     NDropdown: typeof import('naive-ui')['NDropdown']
+    NDynamicInput: typeof import('naive-ui')['NDynamicInput']
     NEmpty: typeof import('naive-ui')['NEmpty']
     NFlex: typeof import('naive-ui')['NFlex']
     NForm: typeof import('naive-ui')['NForm']

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

@@ -36,6 +36,9 @@ declare module "@elegant-router/types" {
     "404": "/404";
     "500": "/500";
     "about": "/about";
+    "config": "/config";
+    "config_fright-config": "/config/fright-config";
+    "config_order-splitting": "/config/order-splitting";
     "goods": "/goods";
     "goods_desk-category": "/goods/desk-category";
     "goods_store-goods": "/goods/store-goods";
@@ -145,6 +148,7 @@ declare module "@elegant-router/types" {
     | "404"
     | "500"
     | "about"
+    | "config"
     | "goods"
     | "government"
     | "home"
@@ -181,6 +185,8 @@ declare module "@elegant-router/types" {
     | "iframe-page"
     | "login"
     | "about"
+    | "config_fright-config"
+    | "config_order-splitting"
     | "goods_desk-category"
     | "goods_store-goods"
     | "goods_tag"

+ 104 - 0
src/views/config/fright-config/index.vue

@@ -0,0 +1,104 @@
+<script setup lang="tsx">
+import { NDynamicInput, NInputNumber } from 'naive-ui';
+import { fetchGetAllChannelList } from '@/service/api/goods/store-goods';
+import { useTabStore } from '@/store/modules/tab';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+import { ApiSelect } from '@/components/zt/ApiSelect';
+const tabStore = useTabStore();
+interface Options {
+  value: any;
+  index: number;
+}
+const [registerForm, { getFieldsValue, validate }] = useForm({
+  schemas: [
+    {
+      field: 'name',
+      component: 'NDynamicInput',
+      label: '配送费/运费',
+      render({ model, field }) {
+        return (
+          <div>
+            <div class={'h38px flex items-center'}>
+              <div class={'w-200px text-center'}> 企业 </div>
+              <div class={'ml-3 w-200px text-center'}> 费用(元/每20kg) </div>
+            </div>
+            <NDynamicInput
+              value={model[field]}
+              onUpdate:value={value => (model[field] = value)}
+              v-slots={(row: Options) => handleCreatInput(model, field, row)}
+              onCreate={() => handleAdd()}
+            ></NDynamicInput>
+          </div>
+        );
+      },
+      required: true,
+      defaultValue: [{ Enterprise: null, Cost: 1 }]
+    }
+  ],
+  labelWidth: 120,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1 xl:4 s:1 l:3',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+function handleCreatInput(model: any, field: any, row: Options) {
+  return (
+    <div class={'flex items-center'}>
+      <div class={'w-200px'}>
+        <ApiSelect
+          value={model[field][row.index].Enterprise}
+          api={fetchGetAllChannelList}
+          labelFeild="channelName"
+          valueFeild="id"
+          onUpdate:value={vlaue => {
+            model[field][row.index].Enterprise = vlaue;
+          }}
+        ></ApiSelect>
+      </div>
+      <div class={'ml-3 w-200px'}>
+        <NInputNumber
+          value={model[field][row.index].Cost}
+          min={1}
+          onUpdate:value={value => {
+            model[field][row.index].Cost = value;
+          }}
+        ></NInputNumber>
+      </div>
+    </div>
+  );
+}
+function close() {
+  tabStore.removeTab(tabStore.activeTabId);
+}
+
+function handleAdd() {
+  return {
+    Enterprise: null,
+    Cost: 1
+  };
+}
+async function save() {
+  await validate();
+  const form = getFieldsValue();
+  console.log(form);
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <NCard title="配送费&运费配置" :bordered="false" size="small" segmented class="card-wrapper">
+      <BasicForm @register-form="registerForm"></BasicForm>
+      <template #footer>
+        <NSpace justify="end">
+          <NButton size="small" @click="close">关闭</NButton>
+          <NButton type="primary" size="small" @click="save">保存</NButton>
+        </NSpace>
+      </template>
+    </NCard>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 67 - 0
src/views/config/order-splitting/index.vue

@@ -0,0 +1,67 @@
+<script setup lang="tsx">
+import { NInput } from 'naive-ui';
+import { useTabStore } from '@/store/modules/tab';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+const tabStore = useTabStore();
+
+const [registerForm, { getFieldsValue, validate }] = useForm({
+  schemas: [
+    {
+      field: 'name',
+      component: 'NInput',
+      label: '配送距离',
+      required: true,
+      render({ model, field }) {
+        return (
+          <NInput
+            placeholder={'请输入配送距离'}
+            value={model[field]}
+            onUpdate:value={val => (model[field] = val)}
+            v-slots={{ prefix: () => '大于', suffix: () => 'km' }}
+          ></NInput>
+        );
+      }
+    }
+  ],
+  labelWidth: 120,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1 xl:4 s:1 l:3',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+
+function close() {
+  tabStore.removeTab(tabStore.activeTabId);
+}
+
+async function save() {
+  await validate();
+  const form = getFieldsValue();
+  console.log(form);
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <NCard
+      title="分单配置(企业用户才有分单规则,C端用户只走配送,B端用户只走物流)"
+      :bordered="false"
+      size="small"
+      segmented
+      class="card-wrapper"
+    >
+      <BasicForm @register-form="registerForm"></BasicForm>
+      <template #footer>
+        <NSpace justify="end">
+          <NButton size="small" @click="close">关闭</NButton>
+          <NButton type="primary" size="small" @click="save">保存</NButton>
+        </NSpace>
+      </template>
+    </NCard>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 51 - 10
src/views/goods/desk-category/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="tsx">
-import { nextTick, ref } from 'vue';
-import { NButton, NImage, type UploadFileInfo } from 'naive-ui';
+import { nextTick, ref, useTemplateRef } from 'vue';
+import { NButton, NImage } from 'naive-ui';
 import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
 import {
   fetchGategoryImport,
@@ -12,11 +12,9 @@ import { fetchGetAllTagList } from '@/service/api/goods/tag';
 import { useAppStore } from '@/store/modules/app';
 import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
 import { useForm } from '@/components/zt/Form/hooks/useForm';
-import type { ModalMethods } from '@/components/zt/Modal/types';
 const appStore = useAppStore();
 const deskData = ref<Api.goods.ShopCategory[]>([]);
 const loading = ref(false);
-const fileList = ref<UploadFileInfo[]>([]);
 const [registerSearchForm, { getFieldsValue: getSearchForm }] = useForm({
   schemas: [
     {
@@ -31,7 +29,7 @@ const [registerSearchForm, { getFieldsValue: getSearchForm }] = useForm({
       componentProps: {
         api: fetchGetAllStoreList,
         labelFeild: 'shopName',
-        valueFeild: 'hbStationId',
+        valueFeild: 'shopId',
         onUpdateValue: () => {
           nextTick(() => {
             getData();
@@ -48,7 +46,7 @@ const [registerSearchForm, { getFieldsValue: getSearchForm }] = useForm({
   },
   collapsedRows: 1
 });
-const importTemplateRef = ref<ModalMethods>();
+const importTemplateRef = useTemplateRef('importTemplateRef');
 const tableColumns: NaiveUI.TableColumn<InternalRowData>[] = [
   {
     title: '分类名称',
@@ -146,8 +144,8 @@ const [
     }
   }
 });
-async function handleSubmit() {
-  const { error } = await fetchGategoryImport(fileList.value[0].file as File);
+async function handleSubmit(file: File) {
+  const { error } = await fetchGategoryImport(file);
   if (!error) {
     importTemplateRef.value?.closeModal();
   }
@@ -155,13 +153,13 @@ async function handleSubmit() {
 }
 function edit(row: Recordable) {
   openModalForm(row);
-  setModalFormValue({ ...row });
+  setModalFormValue({ ...row, label: Number(row.label) });
 }
 
 async function getData() {
   const { data, error } = await fetchGetDeskCategoryList(getSearchForm());
   if (!error) {
-    deskData.value = data;
+    deskData.value = buildTree(data);
   }
 }
 async function handleSubmitForm() {
@@ -175,6 +173,49 @@ async function handleSubmitForm() {
 function handleOpen() {
   importTemplateRef.value?.openModal();
 }
+interface BuildTreeOptions {
+  idKey?: string;
+  pidKey?: string;
+  childrenKey?: string;
+}
+
+function buildTree<T>(items: T[], options: BuildTreeOptions = {}): T[] {
+  const { idKey = 'id', pidKey = 'pid', childrenKey = 'children' } = options;
+
+  const itemMap = new Map<number, T>();
+
+  items.forEach(item => {
+    const id = item[idKey as keyof T];
+    if (typeof id === 'number') {
+      itemMap.set(id, { ...item, [childrenKey]: [] });
+    }
+  });
+
+  const tree: T[] = [];
+
+  items.forEach(item => {
+    const id = item[idKey as keyof T];
+    const pid = item[pidKey as keyof T];
+
+    if (typeof id !== 'number' || typeof pid !== 'number') {
+      return;
+    }
+
+    const currentNode = itemMap.get(id);
+    if (!currentNode) return;
+
+    if (pid === 0) {
+      tree.push(currentNode);
+    } else {
+      const parentNode = itemMap.get(pid);
+      if (parentNode && parentNode[childrenKey as keyof T]) {
+        (parentNode[childrenKey as keyof T] as T[]).push(currentNode);
+      }
+    }
+  });
+
+  return tree;
+}
 </script>
 
 <template>

+ 13 - 10
src/views/goods/store-goods/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { computed, ref } from 'vue';
+import { computed, ref, useTemplateRef } from 'vue';
 import { NButton, NImage, NInputNumber, NSelect } from 'naive-ui';
 import dayjs from 'dayjs';
 import { fetchGetAllStoreList } from '@/service/api/goods/desk-category';
@@ -11,11 +11,10 @@ import {
 } from '@/service/api/goods/store-goods';
 import { areAllItemsAllFieldsFilled } from '@/utils/zt';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
-import type { ModalMethods } from '@/components/zt/Modal/types';
 import SvgIcon from '@/components/custom/svg-icon.vue';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 type Price = { channelId: number | undefined; channelProdPrice: number; id: number };
-const importTemplateRef = ref<ModalMethods>();
+const importTemplateRef = useTemplateRef('importTemplateRef');
 const options = ref<Api.goods.Channel[]>([]);
 const TypeName = ['企业用户', 'B端用户', 'C端用户'];
 
@@ -65,10 +64,6 @@ const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
     width: 120,
     ellipsis: {
       tooltip: true
-    },
-    render: row => {
-      const nameList = row.channelProdList?.map(it => it.shopName);
-      return <div>{nameList?.join(',') || '--'}</div>;
     }
   },
   {
@@ -203,7 +198,7 @@ const PriceColumns: NaiveUI.TableColumn<Price>[] = [
 ];
 const PriceData = ref<Price[]>([]);
 const selectData = ref<Api.goods.ShopSku>();
-const [registerTable, { getTableCheckedRowKeys, refresh, getTableData }] = useTable({
+const [registerTable, { getTableCheckedRowKeys, refresh, getTableData, getSeachForm }] = useTable({
   searchFormConfig: {
     schemas: [
       {
@@ -213,7 +208,7 @@ const [registerTable, { getTableCheckedRowKeys, refresh, getTableData }] = useTa
         componentProps: {
           api: fetchGetAllStoreList,
           labelFeild: 'shopName',
-          valueFeild: 'hbStationId'
+          valueFeild: 'shopId'
         }
       },
       {
@@ -330,6 +325,14 @@ async function getData() {
   }
 }
 getData();
+async function handleExport() {
+  const form = getSeachForm();
+  // 将form对象转换为查询参数字符串
+  const queryParams = new URLSearchParams(form).toString();
+  const baseUrl = `${import.meta.env.VITE_SERVICE_BASE_URL}/platform/channelProd/export`;
+  const fullUrl = queryParams ? `${baseUrl}?${queryParams}` : baseUrl;
+  window.open(fullUrl, '_blank');
+}
 </script>
 
 <template>
@@ -341,7 +344,7 @@ getData();
       <template #prefix>
         <NSpace>
           <NButton size="small" @click="openImportModal">导入商品销售渠道及价格</NButton>
-          <NButton size="small" :disabled="tableData.length == 0">导出全部</NButton>
+          <NButton size="small" :disabled="tableData.length == 0" @click="handleExport">导出全部</NButton>
           <NButton size="small" :disabled="isDisabledExport">导出选中数据</NButton>
           <NButton size="small">修改记录</NButton>
         </NSpace>

+ 2 - 3
src/views/government/points/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { ref } from 'vue';
+import { ref, useTemplateRef } from 'vue';
 import {
   fetchGetFailPointsList,
   fetchGetPointsList,
@@ -8,10 +8,9 @@ import {
 } from '@/service/api/government/points';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
-import type { ModalMethods } from '@/components/zt/Modal/types';
 import type { FormSchema } from '@/components/zt/Form/types/form';
 import SVGIcon from '@/components/custom/svg-icon.vue';
-const importTemplateRef = ref<ModalMethods>();
+const importTemplateRef = useTemplateRef('importTemplateRef');
 const ModalColumns: NaiveUI.TableColumn<Api.government.PointsRecharge>[] = [
   {
     key: 'channelName',

+ 2 - 3
src/views/government/user-list/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { ref } from 'vue';
+import { useTemplateRef } from 'vue';
 import { NButton, NPopconfirm, NSwitch } from 'naive-ui';
 import {
   fetchAddUser,
@@ -12,11 +12,10 @@ import {
 import { fetchGetAllChannelList } from '@/service/api/goods/store-goods';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 import { useModalFrom } from '@/components/zt/ModalForm/hooks/useModalForm';
-import type { ModalMethods } from '@/components/zt/Modal/types';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 import SVGIcon from '@/components/custom/svg-icon.vue';
 
-const importTemplateRef = ref<ModalMethods>();
+const importTemplateRef = useTemplateRef('importTemplateRef');
 const columns: NaiveUI.TableColumn<Api.government.userList>[] = [
   {
     key: 'channelName',

+ 115 - 27
src/views/operation/search/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="tsx">
-import { NButton, NPopconfirm } from 'naive-ui';
-import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
+import { NButton, NPopconfirm, NTag } from 'naive-ui';
+import { commonStatus } from '@/constants/business';
 import {
   fetchAddHotSearch,
   fetchDeleteHotSearch,
@@ -9,40 +9,65 @@ import {
 } 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<InternalRowData>[] = [
+const columns: NaiveUI.TableColumn<Api.operation.HotSearch>[] = [
   {
-    key: 'title',
+    key: 'searchName',
     title: '名称',
     align: 'center'
   },
   {
-    key: 'name',
+    key: 'type',
     title: '类型',
+    align: 'center',
+    render: row => {
+      const arr = ['关键词', '热门搜索词', '推荐搜索词'];
+      return arr[row.type - 1] || '暂无数据';
+    }
+  },
+  {
+    key: 'reachName',
+    title: '落地页',
+    align: 'center'
+  },
+  {
+    key: 'jumpUrl',
+    title: '跳转参数',
+    align: 'center'
+  },
+  {
+    key: 'effectiveTime',
+    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 = row.status || 0;
+      const label = $t(commonStatus[status]);
+      return <NTag type={tagMap[status]}>{label}</NTag>;
+    }
   }
-  // {
-  //   key: 'status',
-  //   title: '经营状态',
-  //   align: 'center',
-  //   render: row => {
-  //     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>;
-  //   }
-  // }
 ];
 
 const [registerTable, { refresh, setTableLoading }] = useTable({
   searchFormConfig: {
     schemas: [
       {
-        field: 'name',
-        label: '标签名称',
+        field: 'searchName',
+        label: '名称',
         component: 'NInput'
       }
     ],
@@ -53,7 +78,7 @@ const [registerTable, { refresh, setTableLoading }] = useTable({
   },
   tableConfig: {
     keyField: 'id',
-    title: '标签列表',
+    title: '搜索词列表',
     showAddButton: true
   }
 });
@@ -65,10 +90,10 @@ async function handleDelete(row: Recordable) {
 }
 const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValue }] = useModalFrom({
   modalConfig: {
-    title: '标签 ',
-    width: 400,
+    title: '跳转 ',
+    width: 700,
     isShowHeaderText: true,
-    height: 100
+    height: 400
   },
   formConfig: {
     schemas: [
@@ -79,10 +104,73 @@ const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValu
         show: false
       },
       {
-        field: 'name',
-        label: '标签名称',
+        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: {