2
0
yourname преди 5 месеца
родител
ревизия
92dba2e7af

+ 5 - 1
src/client/api.ts

@@ -1,7 +1,7 @@
 import axios, { isAxiosError } from 'axios';
 import { hc } from 'hono/client'
 import type {
-  AuthRoutes, UserRoutes,
+  AuthRoutes, ExcelTemplatesRoutes, UserRoutes,
 } from '@/server/api';
 
 // 创建 axios 适配器
@@ -60,3 +60,7 @@ export const authClient = hc<AuthRoutes>('/', {
 export const userClient = hc<UserRoutes>('/', {
   fetch: axiosFetch,
 }).api.v1.users;
+
+export const excelTemplateClient = hc<ExcelTemplatesRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1['excel-templates'];

+ 9 - 9
src/client/member/components/ExcelToJson/index.tsx

@@ -13,7 +13,7 @@ import type { UseMutateFunction } from '@tanstack/react-query';
 
 // 为组件定义 Props 接口
 export interface ExcelToJsonProps {
-  initialValue?: Template;
+  initialValue?: Template | null;
   onSave?: UseMutateFunction<any, Error, Partial<Template>, unknown>;
   /** 是否使用默认配置 */
   useDefaultConfig?: boolean;
@@ -21,8 +21,8 @@ export interface ExcelToJsonProps {
 
 const ExcelToJson: React.FC<ExcelToJsonProps> = ({ initialValue, onSave, useDefaultConfig = false }) => {
   const [viewMode, setViewMode] = useState<ViewMode>('table');
-  const [templateName, setTemplateName] = useState<string>(initialValue?.template_name || '');
-  const [templateKey, setTemplateKey] = useState<string>(initialValue?.template_key || '');
+  const [templateName, setTemplateName] = useState<string>(initialValue?.templateName || '');
+  const [templateKey, setTemplateKey] = useState<string>(initialValue?.templateKey || '');
   
   const { 
     jsonData, 
@@ -38,7 +38,7 @@ const ExcelToJson: React.FC<ExcelToJsonProps> = ({ initialValue, onSave, useDefa
     updateSheetConfig,
     setActiveSheet
   } = useSheetConfig(
-    initialValue?.template_config || (useDefaultConfig ? defaultConfig : { sheets: [], activeSheetIndex: 0 }),
+    initialValue?.templateConfig || (useDefaultConfig ? defaultConfig : { sheets: [], activeSheetIndex: 0 }),
     useDefaultConfig
   );
 
@@ -50,9 +50,9 @@ const ExcelToJson: React.FC<ExcelToJsonProps> = ({ initialValue, onSave, useDefa
 
   // 添加对 initialValue 的处理
   useEffect(() => {
-    if (initialValue?.template_config) {
+    if (initialValue?.templateConfig) {
       // 如果有初始值,可以在这里处理
-      console.log('加载已有模板配置', initialValue.template_config);
+      console.log('加载已有模板配置', initialValue.templateConfig);
     }
   }, [initialValue]);
 
@@ -116,9 +116,9 @@ const ExcelToJson: React.FC<ExcelToJsonProps> = ({ initialValue, onSave, useDefa
     if (!onSave) return;
     
     const templateData: Partial<Template> = {
-      template_name: templateName || '新模板',
-      template_key: templateKey || `template_${Date.now()}`,
-      template_config: config,
+      templateName: templateName || '新模板',
+      templateKey: templateKey || `template_${Date.now()}`,
+      templateConfig: config,
     };
     
     if (initialValue?.id) {

+ 39 - 15
src/client/member/pages/TemplateEdit.tsx

@@ -1,36 +1,60 @@
 import { Card, message, Button, Space, Spin } from 'antd';
-import { useQuery, useMutation, useQueryClient ,QueryClient,QueryClientProvider} from '@tanstack/react-query';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import ExcelToJson from '../components/ExcelToJson/index';
-import type { Template } from '@/share/exceltypes';
+import type { ExcelConfig } from '@/client/member/components/ExcelToJson/types';
 import { useNavigate, useParams } from 'react-router';
+import { excelTemplateClient } from '@/client/api';
+import type { InferResponseType, InferRequestType } from 'hono/client';
+
+type TemplateDetail = InferResponseType<typeof excelTemplateClient[':id']['$get'], 200>;
+type CreateTemplateRequest = InferRequestType<typeof excelTemplateClient.$post>['json'];
+type UpdateTemplateRequest = InferRequestType<typeof excelTemplateClient[':id']['$put']>['json'];
 
 function TemplateEdit() {
   const navigate = useNavigate();
-  const { id } = useParams();
+  const { id: idStr } = useParams();
+  const id = Number(idStr);
   
   const queryClient = useQueryClient();
 
-  const { data: template, isLoading } = useQuery<Template>({
+  const { data: template, isLoading } = useQuery({
     queryKey: ['templates', 'detail', id],
     queryFn: async () => {
       if (!id) return null;
-      const response = await requestWithToken({
-        url: `/v1/member/templates?id=${id}`,
-        method: 'GET'
+      const res = await excelTemplateClient[':id'].$get({
+        param: { id }
       });
-      return response.data;
+      if (res.status !== 200) {
+        const data = await res.json()
+        throw new Error(data.message);
+      }
+      return await res.json();
     },
     enabled: !!id
   });
 
   const { mutate: saveTemplate, isPending } = useMutation({
-    mutationFn: async (data: Partial<Template>) => {
-      const response = await requestWithToken({
-        url: id ? `/v1/member/templates?id=${id}` : '/v1/member/templates',
-        method: id ? 'PUT' : 'POST',
-        data: data
-      });
-      return response.data;
+    mutationFn: async (data: CreateTemplateRequest | UpdateTemplateRequest) => {
+      if (id) {
+        const res = await excelTemplateClient[':id'].$put({
+          param: { id },
+          json: data as UpdateTemplateRequest
+        });
+        if (res.status !== 200) {
+          const data = await res.json()
+          throw new Error(data.message);
+        }
+        return await res.json();
+      } else {
+        const res = await excelTemplateClient.$post({
+          json: data as CreateTemplateRequest
+        });
+        if (res.status !== 200) {
+          const data = await res.json()
+          throw new Error(data.message);
+        }
+        return await res.json();
+      }
     },
     onSuccess: () => {
       message.success('保存成功');

+ 2 - 2
src/server/api/excel-templates/[id]/get.ts

@@ -10,9 +10,9 @@ import { authMiddleware } from '@/server/middleware/auth.middleware';
 const excelTemplateService = new ExcelTemplateService(AppDataSource);
 
 const GetParams = z.object({
-  id: z.string().openapi({
+  id: z.coerce.number().openapi({
     param: { name: 'id', in: 'path' },
-    example: '1',
+    example: 1,
     description: '模板ID'
   })
 });

+ 3 - 3
src/server/api/excel-templates/[id]/put.ts

@@ -10,9 +10,9 @@ import { authMiddleware } from '@/server/middleware/auth.middleware';
 const excelTemplateService = new ExcelTemplateService(AppDataSource);
 
 const GetParams = z.object({
-  id: z.string().openapi({
+  id: z.coerce.number().openapi({
     param: { name: 'id', in: 'path' },
-    example: '1',
+    example: 1,
     description: '模板ID'
   })
 });
@@ -32,7 +32,7 @@ const routeDef = createRoute({
             createdAt: true,
             updatedAt: true,
             isDeleted: true 
-          })
+          }).partial()
         }
       }
     }

+ 78 - 2
src/server/modules/excel/excel-template.entity.ts

@@ -1,5 +1,6 @@
 import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
 import { z } from '@hono/zod-openapi';
+import { ExcelConfig } from '@/client/member/components/ExcelToJson/types';
 
 @Entity('excel_templates')
 export class ExcelTemplate {
@@ -30,7 +31,7 @@ export class ExcelTemplate {
     nullable: false,
     comment: 'Excel模板配置(存储header配置、字段映射规则等)'
   })
-  templateConfig!: Record<string, any>;
+  templateConfig!: ExcelConfig;
 
   @Column({
     name: 'created_by',
@@ -78,7 +79,82 @@ export const ExcelTemplateSchema = z.object({
     description: '模板唯一标识键',
     example: 'default_template'
   }),
-  templateConfig: z.record(z.any()).openapi({
+  templateConfig: z.object({
+    sheets: z.array(
+      z.object({
+        headerRowIndex: z.number().int().positive().openapi({
+          example: 1,
+          description: '表头行号'
+        }),
+        orderNumberRow: z.number().int().positive().openapi({
+          example: 2,
+          description: '订单号行号'
+        }),
+        orderNumberCol: z.number().int().positive().openapi({
+          example: 1,
+          description: '订单号列号'
+        }),
+        productNameRow: z.number().int().positive().optional().openapi({
+          example: 3,
+          description: '产品名称行号'
+        }),
+        productNameCol: z.number().int().positive().optional().openapi({
+          example: 2,
+          description: '产品名称列号'
+        }),
+        dataStartRow: z.number().int().positive().openapi({
+          example: 4,
+          description: '数据起始行号'
+        }),
+        endMarker: z.string().openapi({
+          example: '合计',
+          description: '终止标记'
+        }),
+        sheetName: z.string().openapi({
+          example: 'Sheet1',
+          description: '工作表名称'
+        }),
+        exportFields: z.array(z.string()).openapi({
+          example: ['订单号', '产品名称', '数量'],
+          description: '导出字段'
+        }),
+        fieldMappings: z.record(z.string()).openapi({
+          example: { '订单号': 'orderNumber' },
+          description: '字段映射'
+        }),
+        requiredFields: z.array(z.string()).openapi({
+          example: ['订单号'],
+          description: '必需字段'
+        }),
+        isMultiTable: z.boolean().optional().openapi({
+          example: false,
+          description: '是否支持多表格模式'
+        }),
+        multiTableHeaderOffset: z.number().int().optional().openapi({
+          example: 2,
+          description: '多表格模式下,新表格的表头行号偏移量'
+        }),
+        multiTableDataOffset: z.number().int().optional().openapi({
+          example: 1,
+          description: '多表格模式下,新表格的数据起始行偏移量'
+        }),
+        multiTableOrderNumberOffset: z.number().int().optional().openapi({
+          example: -1,
+          description: '多表格模式下,订单号行相对于表头的偏移量'
+        }),
+        multiTableProductNameOffset: z.number().int().optional().openapi({
+          example: 0,
+          description: '多表格模式下,产品名称行相对于表头的偏移量'
+        })
+      })
+    ).openapi({
+      description: '工作表配置列表'
+    }),
+    activeSheetIndex: z.number().int().nonnegative().openapi({
+      example: 0,
+      description: '当前活动的工作表索引'
+    })
+  }).openapi({
     description: 'Excel模板配置'
   }),
   createdBy: z.number().int().openapi({

+ 45 - 55
src/share/exceltypes.ts

@@ -1,58 +1,48 @@
-import type { ExcelConfig } from "@/client/admin/components/ExcelToJson/types";
+import type { ExcelConfig } from "@/client/member/components/ExcelToJson/types";
 
+import { excelTemplateClient } from '@/client/api';
+import type { InferResponseType, InferRequestType } from 'hono/client';
 
+export type Template = InferResponseType<typeof excelTemplateClient[':id']['$get'], 200>;
 
-  // 模板相关类型
-export interface Template {
-    id: number;
-    template_name: string;
-    template_key: string;
-    template_config: ExcelConfig;
-    created_by: number;
-    is_deleted?: number;
-    created_at: string;
-    updated_at: string;
-  }
-  
-  export interface TemplateAddForm {
-    template_name: string;
-    template_key: string;
-    template_config: ExcelConfig;
-    created_by?: number;
-  }
-  
-  export interface TemplateEditForm extends TemplateAddForm {
-    id: number;
-  }
-  
-  // API 测试记录
-  export interface ApiTestRecord {
-    method: string;
-    url: string;
-    contentType: string;
-    headers?: string;
-    body?: string;
-    timestamp: string;
-    status?: number;
-    response?: any;
-  }
-  
-  // Excel转换API相关接口
-  export interface ConvertRequest {
-    templateId?: string;     // 模板ID,如果提供则使用模板配置
-    input: string;          // 文件URL或Base64编码
-    config?: any;           // 可选的自定义配置,覆盖模板配置
-  }
-  
-  export interface ConvertResponse {
-    success: boolean;
-    data?: { 
-      [sheetName: string]: any[] 
-    };
-    warnings?: string[];
-    availableFields?: { 
-      [sheetName: string]: string[] 
-    };
-    totalTables?: number;
-  } 
-  
+export interface TemplateAddForm {
+  template_name: string;
+  template_key: string;
+  template_config: ExcelConfig;
+  created_by?: number;
+}
+
+export interface TemplateEditForm extends TemplateAddForm {
+  id: number;
+}
+
+// API 测试记录
+export interface ApiTestRecord {
+  method: string;
+  url: string;
+  contentType: string;
+  headers?: string;
+  body?: string;
+  timestamp: string;
+  status?: number;
+  response?: any;
+}
+
+// Excel转换API相关接口
+export interface ConvertRequest {
+  templateId?: string;     // 模板ID,如果提供则使用模板配置
+  input: string;          // 文件URL或Base64编码
+  config?: any;           // 可选的自定义配置,覆盖模板配置
+}
+
+export interface ConvertResponse {
+  success: boolean;
+  data?: {
+    [sheetName: string]: any[]
+  };
+  warnings?: string[];
+  availableFields?: {
+    [sheetName: string]: string[]
+  };
+  totalTables?: number;
+}