From 1fff0a756358db30eab16bb00ccaba879ddce048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Thu, 5 Mar 2026 13:25:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=A4=87=E8=B4=A7?= =?UTF-8?q?=E5=8D=95=E6=A8=A1=E5=9D=97=E5=92=8C=E7=94=A8=E6=88=B7=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/i18n/locales/en.json | 289 ++++++++++++++++++++++ frontend/src/i18n/locales/es.json | 289 ++++++++++++++++++++++ frontend/src/i18n/locales/fr.json | 289 ++++++++++++++++++++++ frontend/src/i18n/locales/ru.json | 289 ++++++++++++++++++++++ frontend/src/i18n/locales/zh.json | 273 ++++++++++++++++++++ frontend/src/main.tsx | 8 +- frontend/src/pages/approval/list.tsx | 30 +-- frontend/src/pages/dashboard/index.tsx | 6 +- frontend/src/pages/login-simple/index.tsx | 29 ++- frontend/src/pages/login/QRCodeLogin.tsx | 33 ++- frontend/src/pages/login/index.tsx | 29 ++- frontend/src/router/index.tsx | 16 ++ frontend/src/services/api.ts | 212 ++++++++++++++++ frontend/src/stores/user.ts | 18 +- frontend/src/types/index.ts | 5 + 15 files changed, 1770 insertions(+), 45 deletions(-) diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 88cb16d..e55c73a 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -186,5 +186,294 @@ "lastMonth": "Last Month", "thisYear": "This Year", "lastYear": "Last Year" + }, + "stock": { + "title": "Stock Order", + "order": "Stock Order", + "final": "Final Stock Order", + "entry": "Stock Entry", + "list": "Stock Order List", + "detail": "Stock Order Detail", + "edit": "Edit Stock Order", + "create": "Create Stock Order", + "createFinal": "Create Final", + "view": "View", + "viewFinal": "View Final", + "orderNo": "Order No.", + "piNo": "Alibaba Order/PI No.", + "customer": "Customer", + "customerName": "Customer Name", + "customerSource": "Customer Source", + "customerCountry": "Customer Country", + "salesperson": "Salesperson", + "bizDate": "Date", + "clearanceCountry": "Clearance Destination Country", + "remark": "Remark", + "approvalHistory": "Approval History", + "contractFile": "Contract File", + "piFile": "PI File", + "transactionSlipFile": "Transaction Slip File", + "vehicle": "Vehicle", + "fileN": "File {{num}}", + "totalRecords": "Total {{count}} records", + "deleteFailed": "Failed to delete", + "printFailed": "Failed to print", + "recallFailed": "Failed to recall", + "voidFailed": "Failed to void", + "voidSuccess": "Voided successfully", + "noContractFiles": "No contract files", + "noDetail": "No details", + "noPiFiles": "No PI files", + "noTransactionSlipFiles": "No transaction slip files", + "finalList": "Final List", + "finalDetail": "Final Detail", + "finalDetailTitle": "Final Stock Order Detail", + "viewDetails": "View Details", + "viewApproval": "View Approval", + "recallApproval": "Recall Approval", + "status": { + "draft": "Draft", + "pending": "Pending Approval", + "approved": "Approved", + "rejected": "Rejected", + "recalled": "Recalled", + "voided": "Voided" + }, + "actions": { + "create": "Create", + "edit": "Edit", + "delete": "Delete", + "submit": "Submit", + "recall": "Recall", + "void": "Void", + "print": "Print", + "copy": "Copy", + "view": "View", + "save": "Save", + "cancel": "Cancel", + "add": "Add", + "recreate": "Recreate" + }, + "detail": { + "baseInfo": "Basic Information", + "orderDetail": "Order Details", + "transportInfo": "Transport Details", + "paymentInfo": "Planned Payment", + "attachments": "Attachments", + "contract": "Contract Files", + "piDoc": "PI Files", + "transactionSlip": "Transaction Slip Files", + "totalCif": "Total CIF", + "contractRate": "Contract Exchange Rate" + }, + "item": { + "model": "Model", + "exteriorColor": "Exterior Color", + "interiorColor": "Interior Color", + "mileage": "Mileage", + "vin": "VIN", + "unitPrice": "Collection Price", + "clearancePrice": "Clearance Price", + "carPrice": "Car Price", + "taxRefund": "Tax Refund", + "profit": "Profit", + "officialPrice": "Official Guide Price", + "discount": "Discount", + "listingPrice": "Used Listing Price", + "reduction": "Used Reduction", + "totalPrice": "Total Price", + "increasePrice": "Price Increase" + }, + "transport": { + "declarant": "Clearance Agent", + "etaShipDate": "Estimated Ship Date", + "domesticPort": "Domestic Port", + "shippingMethod": "Shipping Method", + "tradeTerm": "Trade Term", + "clearancePort": "Clearance Port", + "latestArrival": "Latest Arrival Date" + }, + "payment": { + "remitter": "Remitter", + "remitCountry": "Remitter Country", + "remitDate": "Remittance Date", + "account": "Account", + "amount": "Amount", + "paymentItem": "Payment {{num}}" + }, + "fees": { + "handlingFee": "Handling Fee", + "retrofitFee": "Retrofit Fee", + "fobTransportFee": "FOB Freight", + "portMiscFee": "Port Miscellaneous Fee", + "oceanFreight": "Ocean Freight", + "domesticFreight": "Domestic Freight", + "commission": "Commission", + "purchaseTax": "Purchase Tax", + "taxRefund": "Tax Refund", + "accessoryPrice": "Accessory Price", + "retentionPrice": "Retention Account", + "carPrice": "Car Price", + "increasePrice": "Price Increase", + "clearancePrice": "Clearance Price", + "profit": "Profit", + "officialPrice": "Official Guide Price", + "discount": "Discount", + "listingPrice": "Used Listing Price", + "reduction": "Used Reduction", + "totalPrice": "Total" + }, + "summary": { + "summaryInfo": "Summary Information", + "profit": "Profit", + "totalProfit": "Total Profit" + }, + "dates": { + "pickUpDate": "Pick-up Date", + "collectAllDate": "All Collected Date" + }, + "otherAttachments": "Other Attachments", + "searchPlaceholder": "Search customer name", + "empty": "No stock orders", + "emptyFinal": "No final stock orders", + "totalRecords": "Total {{count}} records", + "confirmDelete": "Are you sure to delete this stock order?", + "confirmRecall": "Are you sure to recall this stock order?", + "confirmVoid": "Are you sure to void this stock order?", + "confirmSubmit": "Confirm to submit?", + "deleteSuccess": "Deleted successfully", + "submitSuccess": "Submitted successfully", + "recallSuccess": "Recalled successfully", + "voidSuccess": "Voided successfully", + "saveSuccess": "Saved successfully", + "loadFailed": "Failed to load", + "loadOrderFailed": "Failed to load original order data", + "loadOrderSuccess": "Original order data loaded", + "saveFailed": "Failed to save", + "submitFailed": "Failed to submit", + "fileUploadSuccess": "File uploaded successfully!", + "fileUploadFailed": "File upload failed!", + "fileDeleted": "File deleted", + "uploading": "Uploading...", + "submitting": "Submitting...", + "atLeastOneItem": "At least one item required", + "atLeastOnePayment": "At least one payment required", + "deleteFailed": "Failed to delete", + "recallFailed": "Failed to recall", + "voidFailed": "Failed to void", + "printFailed": "Failed to print", + "noPrintFile": "No print file available", + "viewDetails": "View Details", + "viewApproval": "View Approval", + "submitForApproval": "Submit for Approval", + "recallApproval": "Recall Approval", + "loading": "Loading...", + "fileN": "File {{num}}", + "contractFile": "Contract File", + "piFile": "PI File", + "transactionSlipFile": "Transaction Slip File", + "fileUpload": { + "contract": "Upload Contract", + "piDoc": "Upload PI", + "transactionSlip": "Upload Transaction Slip", + "clickToUpload": "Click to upload", + "supportMultiple": "Support multiple file upload", + "preview": "Preview", + "download": "Download", + "delete": "Delete", + "fileTypeAccept": "Only {{accept}} format files are allowed!", + "fileSizeMax": "File size cannot exceed {{maxSize}}MB!" + }, + "validation": { + "enterPiNo": "Please enter Alibaba Order/PI No.", + "selectDate": "Please select date", + "selectClearanceCountry": "Please select clearance destination country", + "selectCustomer": "Please select customer", + "enterDeclarant": "Please enter clearance agent", + "selectEtaShipDate": "Please select estimated ship date", + "selectDomesticPort": "Please select domestic port", + "selectShippingMethod": "Please select shipping method", + "selectTradeTerm": "Please select trade term", + "selectClearancePort": "Please select clearance port", + "selectLatestArrival": "Please select latest arrival date", + "selectAllVehicles": "Please select vehicles for all models", + "uploadContract": "Please upload contract file", + "uploadPiDoc": "Please upload PI file", + "uploadTransactionSlip": "Please upload transaction slip file", + "selectPickUpDate": "Please select pick-up date", + "selectCollectAllDate": "Please select all collected date" + }, + "placeholder": { + "autoCalculate": "Auto Calculate", + "enterPiNo": "Please enter Alibaba Order/PI No.", + "selectDate": "Please select date", + "selectClearanceCountry": "Please select clearance destination country", + "selectCustomer": "Please select customer", + "autoFillCustomerSource": "Auto-filled after selecting customer", + "autoFillCustomerCountry": "Auto-filled after selecting customer", + "selectVehicle": "Please select vehicle model", + "selectExteriorColor": "Please select exterior color", + "selectInteriorColor": "Please select interior color", + "newCarMileage": "0 for new car", + "enterVin": "Please enter VIN", + "enterOfficialPrice": "Please enter official guide price", + "enterListingPrice": "Please enter used listing price", + "enterDiscount": "Please enter discount amount", + "enterAmount": "Please enter", + "enterIncreasePrice": "Please enter price increase", + "enterUnitPrice": "Please enter collection price", + "calculatingClearancePrice": "Calculating clearance price", + "calculatingCarPrice": "Calculating car price", + "calculatingTaxRefund": "Calculating estimated tax refund", + "calculatingProfit": "Calculating profit", + "enterRemark": "Please enter remark", + "enterHandlingFee": "Please enter", + "enterContractRate": "Please enter contract exchange rate", + "enterDeclarant": "Please enter clearance agent", + "selectDomesticPort": "Please select domestic port", + "selectShippingMethod": "Please select shipping method", + "selectTradeTerm": "Please select trade term", + "selectClearancePort": "Please select clearance port", + "enterRemitter": "Please enter remitter", + "selectRemitCountry": "Please select remitter country", + "selectAccount": "Please select account", + "enterAmount": "Please enter amount", + "searchCustomer": "Search customer", + "searchVehicle": "Search vehicle", + "selectPickUpDate": "Please select pick-up date", + "selectCollectAllDate": "Please select all collected date", + "enterMileage": "Please enter mileage" + }, + "entry": { + "edit": "Edit Stock Order", + "copy": "Copy Stock Order", + "title": "Stock Entry", + "baseInfo": "Basic Information", + "baseInfoReadOnly": "Basic Information (Read Only)", + "orderDetail": "Order Details", + "transportInfo": "Transport Details", + "paymentInfo": "Planned Payment", + "fileUpload": "File Upload", + "saveDraft": "Save Draft", + "submitApproval": "Submit for Approval", + "createFinal": "Create Final", + "detail": "Detail", + "totalPrice": "Total", + "unitPrice": "Unit Price", + "carPrice": "Car Price", + "profit": "Profit", + "officialPrice": "Official Guide Price", + "discount": "Discount", + "listingPrice": "Used Listing Price", + "reduction": "Used Reduction", + "add": "Add", + "copy": "Copy" + }, + "select": { + "customer": "Select Customer", + "vehicle": "Select Vehicle", + "year": "Year", + "msrp": "Guide Price" + } } } diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json index d8fcaf7..f145a1a 100644 --- a/frontend/src/i18n/locales/es.json +++ b/frontend/src/i18n/locales/es.json @@ -161,5 +161,294 @@ "lastMonth": "Mes Pasado", "thisYear": "Este Año", "lastYear": "Año Pasado" + }, + "stock": { + "title": "Pedido de Stock", + "order": "Pedido de Stock", + "final": "Pedido Final", + "entry": "Entrada de Stock", + "list": "Lista de Pedidos", + "detail": "Detalle del Pedido", + "edit": "Editar Pedido", + "create": "Crear Pedido", + "createFinal": "Crear Final", + "view": "Ver", + "viewFinal": "Ver Final", + "orderNo": "Nº Pedido", + "piNo": "Pedido Alibaba/Nº PI", + "customer": "Cliente", + "customerName": "Nombre del Cliente", + "customerSource": "Fuente del Cliente", + "customerCountry": "País del Cliente", + "salesperson": "Vendedor", + "bizDate": "Fecha", + "clearanceCountry": "País de Destino Aduanero", + "remark": "Observación", + "approvalHistory": "Historial de Aprobación", + "contractFile": "Archivo de Contrato", + "piFile": "Archivo PI", + "transactionSlipFile": "Justificante de Transacción", + "vehicle": "Vehículo", + "fileN": "Archivo {{num}}", + "totalRecords": "{{count}} registros en total", + "deleteFailed": "Error al eliminar", + "printFailed": "Error al imprimir", + "recallFailed": "Error al recuperar", + "voidFailed": "Error al anular", + "voidSuccess": "Anulado con éxito", + "noContractFiles": "Sin archivos de contrato", + "noDetail": "Sin detalles", + "noPiFiles": "Sin archivos PI", + "noTransactionSlipFiles": "Sin justificantes de transacción", + "finalList": "Lista Final", + "finalDetail": "Detalle Final", + "finalDetailTitle": "Detalle del Pedido Final", + "viewDetails": "Ver Detalles", + "viewApproval": "Ver Aprobación", + "recallApproval": "Recuperar Aprobación", + "status": { + "draft": "Borrador", + "pending": "Pendiente de Aprobación", + "approved": "Aprobado", + "rejected": "Rechazado", + "recalled": "Recuperado", + "voided": "Anulado" + }, + "actions": { + "create": "Crear", + "edit": "Editar", + "delete": "Eliminar", + "submit": "Enviar", + "recall": "Recuperar", + "void": "Anular", + "print": "Imprimir", + "copy": "Copiar", + "view": "Ver", + "save": "Guardar", + "cancel": "Cancelar", + "add": "Agregar", + "recreate": "Recrear" + }, + "detail": { + "baseInfo": "Información Básica", + "orderDetail": "Detalles del Pedido", + "transportInfo": "Detalles de Transporte", + "paymentInfo": "Pago Planificado", + "attachments": "Adjuntos", + "contract": "Archivos de Contrato", + "piDoc": "Archivos PI", + "transactionSlip": "Justificantes de Transacción", + "totalCif": "CIF Total", + "contractRate": "Tipo de Cambio del Contrato" + }, + "item": { + "model": "Modelo", + "exteriorColor": "Color Exterior", + "interiorColor": "Color Interior", + "mileage": "Kilometraje", + "vin": "VIN", + "unitPrice": "Precio de Cobro", + "clearancePrice": "Precio de Aduana", + "carPrice": "Precio del Coche", + "taxRefund": "Devolución de Impuestos", + "profit": "Beneficio", + "officialPrice": "Precio Oficial", + "discount": "Descuento", + "listingPrice": "Precio de Segunda Mano", + "reduction": "Reducción Segunda Mano", + "totalPrice": "Precio Total", + "increasePrice": "Incremento de Precio" + }, + "transport": { + "declarant": "Agente de Aduanas", + "etaShipDate": "Fecha Estimada de Envío", + "domesticPort": "Puerto Nacional", + "shippingMethod": "Método de Envío", + "tradeTerm": "Incoterms", + "clearancePort": "Puerto de Aduana", + "latestArrival": "Última Fecha de Llegada" + }, + "payment": { + "remitter": "Remitente", + "remitCountry": "País del Remitente", + "remitDate": "Fecha de Transferencia", + "account": "Cuenta", + "amount": "Importe", + "paymentItem": "Pago {{num}}" + }, + "fees": { + "handlingFee": "Tarifa de Gestión", + "retrofitFee": "Tarifa de Adaptación", + "fobTransportFee": "Transporte FOB", + "portMiscFee": "Gastos Portuarios", + "oceanFreight": "Flete Marítimo", + "domesticFreight": "Flete Nacional", + "commission": "Comisión", + "purchaseTax": "Impuesto de Compra", + "taxRefund": "Devolución de Impuestos", + "accessoryPrice": "Precio de Accesorios", + "retentionPrice": "Retención en Cuenta", + "carPrice": "Precio del Coche", + "increasePrice": "Incremento de Precio", + "clearancePrice": "Precio de Aduana", + "profit": "Beneficio", + "officialPrice": "Precio Oficial", + "discount": "Descuento", + "listingPrice": "Precio de Segunda Mano", + "reduction": "Reducción Segunda Mano", + "totalPrice": "Precio Total" + }, + "summary": { + "summaryInfo": "Resumen de Información", + "profit": "Beneficio", + "totalProfit": "Beneficio Total" + }, + "dates": { + "pickUpDate": "Fecha de Recogida", + "collectAllDate": "Fecha de Recolección Completa" + }, + "otherAttachments": "Otros Adjuntos", + "searchPlaceholder": "Buscar nombre del cliente", + "empty": "Sin pedidos", + "emptyFinal": "Sin pedidos finales", + "totalRecords": "{{count}} registros en total", + "confirmDelete": "¿Está seguro de eliminar este pedido?", + "confirmRecall": "¿Está seguro de recuperar este pedido?", + "confirmVoid": "¿Está seguro de anular este pedido?", + "confirmSubmit": "¿Confirmar envío?", + "deleteSuccess": "Eliminado con éxito", + "submitSuccess": "Enviado con éxito", + "recallSuccess": "Recuperado con éxito", + "voidSuccess": "Anulado con éxito", + "saveSuccess": "Guardado con éxito", + "loadFailed": "Error al cargar", + "loadOrderFailed": "Error al cargar datos del pedido original", + "loadOrderSuccess": "Datos del pedido original cargados", + "saveFailed": "Error al guardar", + "submitFailed": "Error al enviar", + "fileUploadSuccess": "¡Archivo subido con éxito!", + "fileUploadFailed": "¡Error al subir el archivo!", + "fileDeleted": "Archivo eliminado", + "uploading": "Subiendo...", + "submitting": "Enviando...", + "atLeastOneItem": "Se requiere al menos un artículo", + "atLeastOnePayment": "Se requiere al menos un pago", + "deleteFailed": "Error al eliminar", + "recallFailed": "Error al recuperar", + "voidFailed": "Error al anular", + "printFailed": "Error al imprimir", + "noPrintFile": "Archivo de impresión no disponible", + "viewDetails": "Ver Detalles", + "viewApproval": "Ver Aprobación", + "submitForApproval": "Enviar para Aprobación", + "recallApproval": "Recuperar Aprobación", + "loading": "Cargando...", + "fileN": "Archivo {{num}}", + "contractFile": "Archivo de Contrato", + "piFile": "Archivo PI", + "transactionSlipFile": "Justificante de Transacción", + "fileUpload": { + "contract": "Subir Contrato", + "piDoc": "Subir PI", + "transactionSlip": "Subir Justificante", + "clickToUpload": "Haz clic para subir", + "supportMultiple": "Soporte para carga múltiple", + "preview": "Vista Previa", + "download": "Descargar", + "delete": "Eliminar", + "fileTypeAccept": "¡Solo se permiten archivos {{accept}}!", + "fileSizeMax": "¡El tamaño del archivo no puede exceder {{maxSize}}MB!" + }, + "validation": { + "enterPiNo": "Por favor ingrese el Pedido Alibaba/Nº PI", + "selectDate": "Por favor seleccione fecha", + "selectClearanceCountry": "Por favor seleccione país de destino aduanero", + "selectCustomer": "Por favor seleccione cliente", + "enterDeclarant": "Por favor ingrese el agente de aduanas", + "selectEtaShipDate": "Por favor seleccione fecha estimada de envío", + "selectDomesticPort": "Por favor seleccione puerto nacional", + "selectShippingMethod": "Por favor seleccione método de envío", + "selectTradeTerm": "Por favor seleccione Incoterms", + "selectClearancePort": "Por favor seleccione puerto de aduana", + "selectLatestArrival": "Por favor seleccione última fecha de llegada", + "selectAllVehicles": "Por favor seleccione vehículos para todos los modelos", + "uploadContract": "Por favor suba el archivo de contrato", + "uploadPiDoc": "Por favor suba el archivo PI", + "uploadTransactionSlip": "Por favor suba el justificante de transacción", + "selectPickUpDate": "Por favor seleccione fecha de recogida", + "selectCollectAllDate": "Por favor seleccione fecha de recolección completa" + }, + "placeholder": { + "autoCalculate": "Cálculo Automático", + "enterPiNo": "Por favor ingrese el Pedido Alibaba/Nº PI", + "selectDate": "Por favor seleccione fecha", + "selectClearanceCountry": "Por favor seleccione país de destino aduanero", + "selectCustomer": "Por favor seleccione cliente", + "autoFillCustomerSource": "Autocompletado al seleccionar cliente", + "autoFillCustomerCountry": "Autocompletado al seleccionar cliente", + "selectVehicle": "Por favor seleccione modelo de vehículo", + "selectExteriorColor": "Por favor seleccione color exterior", + "selectInteriorColor": "Por favor seleccione color interior", + "newCarMileage": "0 para vehículo nuevo", + "enterVin": "Por favor ingrese VIN", + "enterOfficialPrice": "Por favor ingrese precio oficial", + "enterListingPrice": "Por favor ingrese precio de segunda mano", + "enterDiscount": "Por favor ingrese monto de descuento", + "enterAmount": "Por favor ingrese", + "enterIncreasePrice": "Por favor ingrese incremento de precio", + "enterUnitPrice": "Por favor ingrese precio de cobro", + "calculatingClearancePrice": "Calculando precio de aduana", + "calculatingCarPrice": "Calculando precio del coche", + "calculatingTaxRefund": "Calculando devolución de impuestos estimada", + "calculatingProfit": "Calculando beneficio", + "enterRemark": "Por favor ingrese observación", + "enterHandlingFee": "Por favor ingrese", + "enterContractRate": "Por favor ingrese tipo de cambio del contrato", + "enterDeclarant": "Por favor ingrese agente de aduanas", + "selectDomesticPort": "Por favor seleccione puerto nacional", + "selectShippingMethod": "Por favor seleccione método de envío", + "selectTradeTerm": "Por favor seleccione Incoterms", + "selectClearancePort": "Por favor seleccione puerto de aduana", + "enterRemitter": "Por favor ingrese remitente", + "selectRemitCountry": "Por favor seleccione país del remitente", + "selectAccount": "Por favor seleccione cuenta", + "enterAmount": "Por favor ingrese monto", + "searchCustomer": "Buscar cliente", + "searchVehicle": "Buscar vehículo", + "selectPickUpDate": "Por favor seleccione fecha de recogida", + "selectCollectAllDate": "Por favor seleccione fecha de recolección completa", + "enterMileage": "Por favor ingrese kilometraje" + }, + "entry": { + "edit": "Editar Pedido", + "copy": "Copiar Pedido", + "title": "Entrada de Stock", + "baseInfo": "Información Básica", + "baseInfoReadOnly": "Información Básica (Solo Lectura)", + "orderDetail": "Detalles del Pedido", + "transportInfo": "Detalles de Transporte", + "paymentInfo": "Pago Planificado", + "fileUpload": "Subir Archivos", + "saveDraft": "Guardar Borrador", + "submitApproval": "Enviar para Aprobación", + "createFinal": "Crear Final", + "detail": "Detalle", + "totalPrice": "Total", + "unitPrice": "Precio Unitario", + "carPrice": "Precio del Coche", + "profit": "Beneficio", + "officialPrice": "Precio Oficial", + "discount": "Descuento", + "listingPrice": "Precio de Segunda Mano", + "reduction": "Reducción Segunda Mano", + "add": "Agregar", + "copy": "Copiar" + }, + "select": { + "customer": "Seleccionar Cliente", + "vehicle": "Seleccionar Vehículo", + "year": "Año", + "msrp": "Precio de Catálogo" + } } } diff --git a/frontend/src/i18n/locales/fr.json b/frontend/src/i18n/locales/fr.json index 0922bdd..5163cae 100644 --- a/frontend/src/i18n/locales/fr.json +++ b/frontend/src/i18n/locales/fr.json @@ -161,5 +161,294 @@ "lastMonth": "Mois Dernier", "thisYear": "Cette Année", "lastYear": "Année Dernière" + }, + "stock": { + "title": "Commande de Stock", + "order": "Commande de Stock", + "final": "Commande Finale", + "entry": "Saisie de Stock", + "list": "Liste des Commandes", + "detail": "Détail de la Commande", + "edit": "Modifier la Commande", + "create": "Créer une Commande", + "createFinal": "Créer Finale", + "view": "Voir", + "viewFinal": "Voir Finale", + "orderNo": "Nº Commande", + "piNo": "Commande Alibaba/Nº PI", + "customer": "Client", + "customerName": "Nom du Client", + "customerSource": "Source du Client", + "customerCountry": "Pays du Client", + "salesperson": "Commercial", + "bizDate": "Date", + "clearanceCountry": "Pays de Dédouanement", + "remark": "Remarque", + "approvalHistory": "Historique d'Approbation", + "contractFile": "Fichier de Contrat", + "piFile": "Fichier PI", + "transactionSlipFile": "Justificatif de Transaction", + "vehicle": "Véhicule", + "fileN": "Fichier {{num}}", + "totalRecords": "{{count}} enregistrements au total", + "deleteFailed": "Échec de la suppression", + "printFailed": "Échec de l'impression", + "recallFailed": "Échec du rappel", + "voidFailed": "Échec de l'annulation", + "voidSuccess": "Annulé avec succès", + "noContractFiles": "Aucun fichier de contrat", + "noDetail": "Aucun détail", + "noPiFiles": "Aucun fichier PI", + "noTransactionSlipFiles": "Aucun justificatif de transaction", + "finalList": "Liste Finale", + "finalDetail": "Détail Final", + "finalDetailTitle": "Détail de la Commande Finale", + "viewDetails": "Voir les Détails", + "viewApproval": "Voir l'Approbation", + "recallApproval": "Rappeler l'Approbation", + "status": { + "draft": "Brouillon", + "pending": "En Attente d'Approbation", + "approved": "Approuvé", + "rejected": "Rejeté", + "recalled": "Rappelé", + "voided": "Annulé" + }, + "actions": { + "create": "Créer", + "edit": "Modifier", + "delete": "Supprimer", + "submit": "Soumettre", + "recall": "Rappeler", + "void": "Annuler", + "print": "Imprimer", + "copy": "Copier", + "view": "Voir", + "save": "Enregistrer", + "cancel": "Annuler", + "add": "Ajouter", + "recreate": "Recréer" + }, + "detail": { + "baseInfo": "Informations de Base", + "orderDetail": "Détails de la Commande", + "transportInfo": "Détails du Transport", + "paymentInfo": "Paiement Planifié", + "attachments": "Pièces Jointes", + "contract": "Fichiers de Contrat", + "piDoc": "Fichiers PI", + "transactionSlip": "Justificatifs de Transaction", + "totalCif": "CIF Total", + "contractRate": "Taux de Change du Contrat" + }, + "item": { + "model": "Modèle", + "exteriorColor": "Couleur Extérieure", + "interiorColor": "Couleur Intérieure", + "mileage": "Kilométrage", + "vin": "VIN", + "unitPrice": "Prix d'Encaissement", + "clearancePrice": "Prix de Douane", + "carPrice": "Prix du Véhicule", + "taxRefund": "Remboursement Fiscal", + "profit": "Bénéfice", + "officialPrice": "Prix Officiel", + "discount": "Remise", + "listingPrice": "Prix d'Occasion", + "reduction": "Réduction d'Occasion", + "totalPrice": "Prix Total", + "increasePrice": "Augmentation de Prix" + }, + "transport": { + "declarant": "Transitaire", + "etaShipDate": "Date d'Expédition Estimée", + "domesticPort": "Port National", + "shippingMethod": "Mode de Transport", + "tradeTerm": "Incoterms", + "clearancePort": "Port de Dédouanement", + "latestArrival": "Dernière Date d'Arrivée" + }, + "payment": { + "remitter": "Émetteur", + "remitCountry": "Pays de l'Émetteur", + "remitDate": "Date du Virement", + "account": "Compte", + "amount": "Montant", + "paymentItem": "Paiement {{num}}" + }, + "fees": { + "handlingFee": "Frais de Traitement", + "retrofitFee": "Frais d'Équipement", + "fobTransportFee": "Transport FOB", + "portMiscFee": "Frais Portuaires", + "oceanFreight": "Fret Maritime", + "domesticFreight": "Fret National", + "commission": "Commission", + "purchaseTax": "Taxe d'Achat", + "taxRefund": "Remboursement Fiscal", + "accessoryPrice": "Prix des Accessoires", + "retentionPrice": "Retenue sur Compte", + "carPrice": "Prix du Véhicule", + "increasePrice": "Augmentation de Prix", + "clearancePrice": "Prix de Douane", + "profit": "Bénéfice", + "officialPrice": "Prix Officiel", + "discount": "Remise", + "listingPrice": "Prix d'Occasion", + "reduction": "Réduction d'Occasion", + "totalPrice": "Prix Total" + }, + "summary": { + "summaryInfo": "Résumé des Informations", + "profit": "Bénéfice", + "totalProfit": "Bénéfice Total" + }, + "dates": { + "pickUpDate": "Date de Prise en Charge", + "collectAllDate": "Date de Collecte Complète" + }, + "otherAttachments": "Autres Pièces Jointes", + "searchPlaceholder": "Rechercher nom du client", + "empty": "Aucune commande", + "emptyFinal": "Aucune commande finale", + "totalRecords": "{{count}} enregistrements au total", + "confirmDelete": "Confirmer la suppression de cette commande?", + "confirmRecall": "Confirmer le rappel de cette commande?", + "confirmVoid": "Confirmer l'annulation de cette commande?", + "confirmSubmit": "Confirmer la soumission?", + "deleteSuccess": "Supprimé avec succès", + "submitSuccess": "Soumis avec succès", + "recallSuccess": "Rappelé avec succès", + "voidSuccess": "Annulé avec succès", + "saveSuccess": "Enregistré avec succès", + "loadFailed": "Échec du chargement", + "loadOrderFailed": "Échec du chargement des données de la commande originale", + "loadOrderSuccess": "Données de la commande originale chargées", + "saveFailed": "Échec de l'enregistrement", + "submitFailed": "Échec de la soumission", + "fileUploadSuccess": "Fichier téléchargé avec succès!", + "fileUploadFailed": "Échec du téléchargement du fichier!", + "fileDeleted": "Fichier supprimé", + "uploading": "Téléchargement...", + "submitting": "Soumission...", + "atLeastOneItem": "Au moins un article requis", + "atLeastOnePayment": "Au moins un paiement requis", + "deleteFailed": "Échec de la suppression", + "recallFailed": "Échec du rappel", + "voidFailed": "Échec de l'annulation", + "printFailed": "Échec de l'impression", + "noPrintFile": "Fichier d'impression non disponible", + "viewDetails": "Voir les Détails", + "viewApproval": "Voir l'Approbation", + "submitForApproval": "Soumettre pour Approbation", + "recallApproval": "Rappeler l'Approbation", + "loading": "Chargement...", + "fileN": "Fichier {{num}}", + "contractFile": "Fichier de Contrat", + "piFile": "Fichier PI", + "transactionSlipFile": "Justificatif de Transaction", + "fileUpload": { + "contract": "Télécharger un Contrat", + "piDoc": "Télécharger un PI", + "transactionSlip": "Télécharger un Justificatif", + "clickToUpload": "Cliquez pour télécharger", + "supportMultiple": "Support du téléchargement multiple", + "preview": "Aperçu", + "download": "Télécharger", + "delete": "Supprimer", + "fileTypeAccept": "Seuls les fichiers {{accept}} sont autorisés!", + "fileSizeMax": "La taille du fichier ne peut pas dépasser {{maxSize}}Mo!" + }, + "validation": { + "enterPiNo": "Veuillez entrer la Commande Alibaba/Nº PI", + "selectDate": "Veuillez sélectionner la date", + "selectClearanceCountry": "Veuillez sélectionner le pays de dédouanement", + "selectCustomer": "Veuillez sélectionner le client", + "enterDeclarant": "Veuillez entrer le transitaire", + "selectEtaShipDate": "Veuillez sélectionner la date d'expédition estimée", + "selectDomesticPort": "Veuillez sélectionner le port national", + "selectShippingMethod": "Veuillez sélectionner le mode de transport", + "selectTradeTerm": "Veuillez sélectionner les Incoterms", + "selectClearancePort": "Veuillez sélectionner le port de dédouanement", + "selectLatestArrival": "Veuillez sélectionner la dernière date d'arrivée", + "selectAllVehicles": "Veuillez sélectionner les véhicules pour tous les modèles", + "uploadContract": "Veuillez télécharger le fichier de contrat", + "uploadPiDoc": "Veuillez télécharger le fichier PI", + "uploadTransactionSlip": "Veuillez télécharger le justificatif de transaction", + "selectPickUpDate": "Veuillez sélectionner la date de prise en charge", + "selectCollectAllDate": "Veuillez sélectionner la date de collecte complète" + }, + "placeholder": { + "autoCalculate": "Calcul Automatique", + "enterPiNo": "Veuillez entrer la Commande Alibaba/Nº PI", + "selectDate": "Veuillez sélectionner la date", + "selectClearanceCountry": "Veuillez sélectionner le pays de dédouanement", + "selectCustomer": "Veuillez sélectionner le client", + "autoFillCustomerSource": "Rempli automatiquement après sélection du client", + "autoFillCustomerCountry": "Rempli automatiquement après sélection du client", + "selectVehicle": "Veuillez sélectionner le modèle de véhicule", + "selectExteriorColor": "Veuillez sélectionner la couleur extérieure", + "selectInteriorColor": "Veuillez sélectionner la couleur intérieure", + "newCarMileage": "0 pour véhicule neuf", + "enterVin": "Veuillez entrer le VIN", + "enterOfficialPrice": "Veuillez entrer le prix officiel", + "enterListingPrice": "Veuillez entrer le prix d'occasion", + "enterDiscount": "Veuillez entrer le montant de la remise", + "enterAmount": "Veuillez entrer", + "enterIncreasePrice": "Veuillez entrer l'augmentation de prix", + "enterUnitPrice": "Veuillez entrer le prix d'encaissement", + "calculatingClearancePrice": "Calcul du prix de douane", + "calculatingCarPrice": "Calcul du prix du véhicule", + "calculatingTaxRefund": "Calcul du remboursement fiscal estimé", + "calculatingProfit": "Calcul du bénéfice", + "enterRemark": "Veuillez entrer une remarque", + "enterHandlingFee": "Veuillez entrer", + "enterContractRate": "Veuillez entrer le taux de change du contrat", + "enterDeclarant": "Veuillez entrer le transitaire", + "selectDomesticPort": "Veuillez sélectionner le port national", + "selectShippingMethod": "Veuillez sélectionner le mode de transport", + "selectTradeTerm": "Veuillez sélectionner les Incoterms", + "selectClearancePort": "Veuillez sélectionner le port de dédouanement", + "enterRemitter": "Veuillez entrer l'émetteur", + "selectRemitCountry": "Veuillez sélectionner le pays de l'émetteur", + "selectAccount": "Veuillez sélectionner le compte", + "enterAmount": "Veuillez entrer le montant", + "searchCustomer": "Rechercher client", + "searchVehicle": "Rechercher véhicule", + "selectPickUpDate": "Veuillez sélectionner la date de prise en charge", + "selectCollectAllDate": "Veuillez sélectionner la date de collecte complète", + "enterMileage": "Veuillez entrer le kilométrage" + }, + "entry": { + "edit": "Modifier la Commande", + "copy": "Copier la Commande", + "title": "Saisie de Stock", + "baseInfo": "Informations de Base", + "baseInfoReadOnly": "Informations de Base (Lecture Seule)", + "orderDetail": "Détails de la Commande", + "transportInfo": "Détails du Transport", + "paymentInfo": "Paiement Planifié", + "fileUpload": "Télécharger Fichiers", + "saveDraft": "Enregistrer le Brouillon", + "submitApproval": "Soumettre pour Approbation", + "createFinal": "Créer Finale", + "detail": "Détail", + "totalPrice": "Total", + "unitPrice": "Prix Unitaire", + "carPrice": "Prix du Véhicule", + "profit": "Bénéfice", + "officialPrice": "Prix Officiel", + "discount": "Remise", + "listingPrice": "Prix d'Occasion", + "reduction": "Réduction d'Occasion", + "add": "Ajouter", + "copy": "Copier" + }, + "select": { + "customer": "Sélectionner Client", + "vehicle": "Sélectionner Véhicule", + "year": "Année", + "msrp": "Prix Catalogue" + } } } diff --git a/frontend/src/i18n/locales/ru.json b/frontend/src/i18n/locales/ru.json index 206daa5..67bf754 100644 --- a/frontend/src/i18n/locales/ru.json +++ b/frontend/src/i18n/locales/ru.json @@ -161,5 +161,294 @@ "lastMonth": "В прошлом месяце", "thisYear": "В этом году", "lastYear": "В прошлом году" + }, + "stock": { + "title": "Заказ на поставку", + "order": "Заказ на поставку", + "final": "Финальный заказ", + "entry": "Ввод заказа", + "list": "Список заказов", + "detail": "Детали заказа", + "edit": "Редактировать заказ", + "create": "Создать заказ", + "createFinal": "Создать финальный", + "view": "Просмотр", + "viewFinal": "Просмотр финального", + "orderNo": "№ Заказа", + "piNo": "Заказ Alibaba/№ PI", + "customer": "Клиент", + "customerName": "Имя клиента", + "customerSource": "Источник клиента", + "customerCountry": "Страна клиента", + "salesperson": "Менеджер", + "bizDate": "Дата", + "clearanceCountry": "Страна таможенной очистки", + "remark": "Примечание", + "approvalHistory": "История согласования", + "contractFile": "Файл контракта", + "piFile": "Файл PI", + "transactionSlipFile": "Банковская квитанция", + "vehicle": "Транспортное средство", + "fileN": "Файл {{num}}", + "totalRecords": "Всего {{count}} записей", + "deleteFailed": "Ошибка удаления", + "printFailed": "Ошибка печати", + "recallFailed": "Ошибка отзыва", + "voidFailed": "Ошибка аннулирования", + "voidSuccess": "Аннулировано успешно", + "noContractFiles": "Нет файлов контракта", + "noDetail": "Нет деталей", + "noPiFiles": "Нет файлов PI", + "noTransactionSlipFiles": "Нет банковских квитанций", + "finalList": "Список финальных", + "finalDetail": "Детали финального", + "finalDetailTitle": "Детали финального заказа", + "viewDetails": "Просмотр деталей", + "viewApproval": "Просмотр согласования", + "recallApproval": "Отозвать согласование", + "status": { + "draft": "Черновик", + "pending": "На согласовании", + "approved": "Согласовано", + "rejected": "Отклонено", + "recalled": "Отозвано", + "voided": "Аннулировано" + }, + "actions": { + "create": "Создать", + "edit": "Редактировать", + "delete": "Удалить", + "submit": "Отправить", + "recall": "Отозвать", + "void": "Аннулировать", + "print": "Печать", + "copy": "Копировать", + "view": "Просмотр", + "save": "Сохранить", + "cancel": "Отмена", + "add": "Добавить", + "recreate": "Пересоздать" + }, + "detail": { + "baseInfo": "Основная информация", + "orderDetail": "Детали заказа", + "transportInfo": "Детали транспортировки", + "paymentInfo": "Планируемая оплата", + "attachments": "Вложения", + "contract": "Файлы контракта", + "piDoc": "Файлы PI", + "transactionSlip": "Банковские квитанции", + "totalCif": "Итого CIF", + "contractRate": "Курс обмена по контракту" + }, + "item": { + "model": "Модель", + "exteriorColor": "Цвет экстерьера", + "interiorColor": "Цвет интерьера", + "mileage": "Пробег", + "vin": "VIN", + "unitPrice": "Цена получения", + "clearancePrice": "Таможенная цена", + "carPrice": "Цена автомобиля", + "taxRefund": "Налоговый возврат", + "profit": "Прибыль", + "officialPrice": "Рекомендуемая цена", + "discount": "Скидка", + "listingPrice": "Цена подержанного", + "reduction": "Уценка подержанного", + "totalPrice": "Общая цена", + "increasePrice": "Наценка" + }, + "transport": { + "declarant": "Таможенный брокер", + "etaShipDate": "Ожидаемая дата отправки", + "domesticPort": "Внутренний порт", + "shippingMethod": "Способ доставки", + "tradeTerm": "Торговые условия", + "clearancePort": "Порт таможенной очистки", + "latestArrival": "Последняя дата прибытия" + }, + "payment": { + "remitter": "Отправитель", + "remitCountry": "Страна отправителя", + "remitDate": "Дата перевода", + "account": "Счет", + "amount": "Сумма", + "paymentItem": "Оплата {{num}}" + }, + "fees": { + "handlingFee": "Комиссия за обработку", + "retrofitFee": "Плата за доработку", + "fobTransportFee": "Транспорт FOB", + "portMiscFee": "Портовые сборы", + "oceanFreight": "Морской фрахт", + "domesticFreight": "Внутренняя перевозка", + "commission": "Комиссия", + "purchaseTax": "Налог на покупку", + "taxRefund": "Налоговый возврат", + "accessoryPrice": "Цена аксессуаров", + "retentionPrice": "Удержание на счете", + "carPrice": "Цена автомобиля", + "increasePrice": "Наценка", + "clearancePrice": "Таможенная цена", + "profit": "Прибыль", + "officialPrice": "Рекомендуемая цена", + "discount": "Скидка", + "listingPrice": "Цена подержанного", + "reduction": "Уценка подержанного", + "totalPrice": "Общая цена" + }, + "summary": { + "summaryInfo": "Сводная информация", + "profit": "Прибыль", + "totalProfit": "Общая прибыль" + }, + "dates": { + "pickUpDate": "Дата получения", + "collectAllDate": "Дата полного сбора" + }, + "otherAttachments": "Другие вложения", + "searchPlaceholder": "Поиск по имени клиента", + "empty": "Нет заказов", + "emptyFinal": "Нет финальных заказов", + "totalRecords": "Всего {{count}} записей", + "confirmDelete": "Вы уверены, что хотите удалить этот заказ?", + "confirmRecall": "Вы уверены, что хотите отозвать этот заказ?", + "confirmVoid": "Вы уверены, что хотите аннулировать этот заказ?", + "confirmSubmit": "Подтвердить отправку?", + "deleteSuccess": "Успешно удалено", + "submitSuccess": "Успешно отправлено", + "recallSuccess": "Успешно отозвано", + "voidSuccess": "Успешно аннулировано", + "saveSuccess": "Успешно сохранено", + "loadFailed": "Ошибка загрузки", + "loadOrderFailed": "Ошибка загрузки данных оригинального заказа", + "loadOrderSuccess": "Данные оригинального заказа загружены", + "saveFailed": "Ошибка сохранения", + "submitFailed": "Ошибка отправки", + "fileUploadSuccess": "Файл успешно загружен!", + "fileUploadFailed": "Ошибка загрузки файла!", + "fileDeleted": "Файл удален", + "uploading": "Загрузка...", + "submitting": "Отправка...", + "atLeastOneItem": "Требуется хотя бы одна позиция", + "atLeastOnePayment": "Требуется хотя бы один платеж", + "deleteFailed": "Ошибка удаления", + "recallFailed": "Ошибка отзыва", + "voidFailed": "Ошибка аннулирования", + "printFailed": "Ошибка печати", + "noPrintFile": "Файл для печати недоступен", + "viewDetails": "Просмотр деталей", + "viewApproval": "Просмотр согласования", + "submitForApproval": "Отправить на согласование", + "recallApproval": "Отозвать согласование", + "loading": "Загрузка...", + "fileN": "Файл {{num}}", + "contractFile": "Файл контракта", + "piFile": "Файл PI", + "transactionSlipFile": "Банковская квитанция", + "fileUpload": { + "contract": "Загрузить контракт", + "piDoc": "Загрузить PI", + "transactionSlip": "Загрузить банковскую квитанцию", + "clickToUpload": "Нажмите для загрузки", + "supportMultiple": "Поддержка загрузки нескольких файлов", + "preview": "Предпросмотр", + "download": "Скачать", + "delete": "Удалить", + "fileTypeAccept": "Разрешены только файлы формата {{accept}}!", + "fileSizeMax": "Размер файла не может превышать {{maxSize}}МБ!" + }, + "validation": { + "enterPiNo": "Пожалуйста, введите номер заказа Alibaba/PI", + "selectDate": "Пожалуйста, выберите дату", + "selectClearanceCountry": "Пожалуйста, выберите страну таможенной очистки", + "selectCustomer": "Пожалуйста, выберите клиента", + "enterDeclarant": "Пожалуйста, введите таможенного брокера", + "selectEtaShipDate": "Пожалуйста, выберите ожидаемую дату отправки", + "selectDomesticPort": "Пожалуйста, выберите внутренний порт", + "selectShippingMethod": "Пожалуйста, выберите способ доставки", + "selectTradeTerm": "Пожалуйста, выберите торговые условия", + "selectClearancePort": "Пожалуйста, выберите порт таможенной очистки", + "selectLatestArrival": "Пожалуйста, выберите последнюю дату прибытия", + "selectAllVehicles": "Пожалуйста, выберите транспортные средства для всех моделей", + "uploadContract": "Пожалуйста, загрузите файл контракта", + "uploadPiDoc": "Пожалуйста, загрузите файл PI", + "uploadTransactionSlip": "Пожалуйста, загрузите банковскую квитанцию", + "selectPickUpDate": "Пожалуйста, выберите дату получения", + "selectCollectAllDate": "Пожалуйста, выберите дату полного сбора" + }, + "placeholder": { + "autoCalculate": "Автоматический расчет", + "enterPiNo": "Пожалуйста, введите номер заказа Alibaba/PI", + "selectDate": "Пожалуйста, выберите дату", + "selectClearanceCountry": "Пожалуйста, выберите страну таможенной очистки", + "selectCustomer": "Пожалуйста, выберите клиента", + "autoFillCustomerSource": "Автозаполнение после выбора клиента", + "autoFillCustomerCountry": "Автозаполнение после выбора клиента", + "selectVehicle": "Пожалуйста, выберите модель транспортного средства", + "selectExteriorColor": "Пожалуйста, выберите цвет экстерьера", + "selectInteriorColor": "Пожалуйста, выберите цвет интерьера", + "newCarMileage": "0 для нового автомобиля", + "enterVin": "Пожалуйста, введите VIN", + "enterOfficialPrice": "Пожалуйста, введите рекомендованную цену", + "enterListingPrice": "Пожалуйста, введите цену подержанного", + "enterDiscount": "Пожалуйста, введите сумму скидки", + "enterAmount": "Пожалуйста, введите", + "enterIncreasePrice": "Пожалуйста, введите наценку", + "enterUnitPrice": "Пожалуйста, введите цену получения", + "calculatingClearancePrice": "Расчет таможенной цены", + "calculatingCarPrice": "Расчет цены автомобиля", + "calculatingTaxRefund": "Расчет ожидаемого налогового возврата", + "calculatingProfit": "Расчет прибыли", + "enterRemark": "Пожалуйста, введите примечание", + "enterHandlingFee": "Пожалуйста, введите", + "enterContractRate": "Пожалуйста, введите курс обмена по контракту", + "enterDeclarant": "Пожалуйста, введите таможенного брокера", + "selectDomesticPort": "Пожалуйста, выберите внутренний порт", + "selectShippingMethod": "Пожалуйста, выберите способ доставки", + "selectTradeTerm": "Пожалуйста, выберите торговые условия", + "selectClearancePort": "Пожалуйста, выберите порт таможенной очистки", + "enterRemitter": "Пожалуйста, введите отправителя", + "selectRemitCountry": "Пожалуйста, выберите страну отправителя", + "selectAccount": "Пожалуйста, выберите счет", + "enterAmount": "Пожалуйста, введите сумму", + "searchCustomer": "Поиск клиента", + "searchVehicle": "Поиск транспортного средства", + "selectPickUpDate": "Пожалуйста, выберите дату получения", + "selectCollectAllDate": "Пожалуйста, выберите дату полного сбора", + "enterMileage": "Пожалуйста, введите пробег" + }, + "entry": { + "edit": "Редактировать заказ", + "copy": "Копировать заказ", + "title": "Ввод заказа", + "baseInfo": "Основная информация", + "baseInfoReadOnly": "Основная информация (только для чтения)", + "orderDetail": "Детали заказа", + "transportInfo": "Детали транспортировки", + "paymentInfo": "Планируемая оплата", + "fileUpload": "Загрузка файлов", + "saveDraft": "Сохранить черновик", + "submitApproval": "Отправить на согласование", + "createFinal": "Создать финальный", + "detail": "Детали", + "totalPrice": "Итого", + "unitPrice": "Цена за единицу", + "carPrice": "Цена автомобиля", + "profit": "Прибыль", + "officialPrice": "Рекомендуемая цена", + "discount": "Скидка", + "listingPrice": "Цена подержанного", + "reduction": "Уценка подержанного", + "add": "Добавить", + "copy": "Копировать" + }, + "select": { + "customer": "Выберите клиента", + "vehicle": "Выберите транспортное средство", + "year": "Год", + "msrp": "Рекомендуемая цена" + } } } diff --git a/frontend/src/i18n/locales/zh.json b/frontend/src/i18n/locales/zh.json index 9209488..26a3489 100644 --- a/frontend/src/i18n/locales/zh.json +++ b/frontend/src/i18n/locales/zh.json @@ -186,5 +186,278 @@ "lastMonth": "上月", "thisYear": "今年", "lastYear": "去年" + }, + "stock": { + "title": "备货单", + "order": "备货单", + "final": "终版备货单", + "entry": "备货录入", + "list": "备货单列表", + "detail": "备货单详情", + "edit": "编辑备货单", + "create": "创建备货单", + "createFinal": "创建终版", + "view": "查看", + "viewFinal": "查看终版", + "orderNo": "备货单号", + "piNo": "阿里订单号/PI号", + "customer": "客户", + "customerName": "客户名称", + "customerSource": "客户来源", + "customerCountry": "客户所在国", + "salesperson": "业务员", + "bizDate": "日期", + "clearanceCountry": "清关目的国", + "remark": "备注", + "approvalHistory": "审批历史", + "contractFile": "合同文件", + "piFile": "PI文件", + "transactionSlipFile": "水单文件", + "vehicle": "车辆", + "fileN": "文件{{num}}", + "totalRecords": "共{{count}}条记录", + "deleteFailed": "删除失败", + "printFailed": "打印失败", + "recallFailed": "撤回失败", + "voidFailed": "作废失败", + "voidSuccess": "作废成功", + "noContractFiles": "暂无合同文件", + "noDetail": "暂无详情", + "noPiFiles": "暂无PI文件", + "noTransactionSlipFiles": "暂无水单文件", + "finalList": "终版列表", + "finalDetail": "终版详情", + "finalDetailTitle": "终版备货单详情", + "viewDetails": "查看详情", + "viewApproval": "查看审批", + "recallApproval": "撤回审批", + "status": { + "draft": "草稿", + "pending": "审批中", + "approved": "已通过", + "rejected": "已拒绝", + "recalled": "已撤回", + "voided": "已作废" + }, + "actions": { + "create": "创建", + "edit": "编辑", + "delete": "删除", + "submit": "提交", + "recall": "撤回", + "void": "作废", + "print": "打印", + "copy": "复制", + "view": "查看", + "save": "保存", + "cancel": "取消", + "add": "添加", + "recreate": "重建" + }, + "detail": { + "baseInfo": "基础信息", + "orderDetail": "订单明细", + "transportInfo": "运输详情", + "paymentInfo": "计划收款信息", + "attachments": "附件", + "contract": "合同文件", + "piDoc": "PI文件", + "transactionSlip": "水单文件", + "totalCif": "合计到岸价", + "contractRate": "合同汇率" + }, + "item": { + "model": "车型", + "exteriorColor": "外饰颜色", + "interiorColor": "内饰颜色", + "mileage": "里程", + "vin": "车架号", + "unitPrice": "收款价", + "clearancePrice": "报关价", + "carPrice": "车价", + "taxRefund": "退税额", + "profit": "利润", + "officialPrice": "官方指导价", + "discount": "优惠", + "listingPrice": "二手挂牌价", + "reduction": "二手减价", + "totalPrice": "总价", + "increasePrice": "加价" + }, + "transport": { + "declarant": "清关人", + "etaShipDate": "预计发送日", + "domesticPort": "国内收货港", + "shippingMethod": "运输方式", + "tradeTerm": "贸易条款", + "clearancePort": "清关目的港", + "latestArrival": "最迟到港时间" + }, + "dates": { + "pickUpDate": "提车日期", + "collectAllDate": "全部收齐日期" + }, + "loading": "加载中...", + "empty": "暂无备货单", + "emptyFinal": "暂无终版备货单", + "confirmDelete": "确定要删除此备货单吗?", + "confirmRecall": "确定要撤回此备货单吗?", + "confirmVoid": "确定要作废此备货单吗?", + "confirmSubmit": "确认提交?", + "deleteSuccess": "删除成功", + "submitSuccess": "提交成功", + "recallSuccess": "撤回成功", + "voidSuccess": "作废成功", + "saveSuccess": "保存成功", + "loadFailed": "加载失败", + "loadOrderFailed": "加载原订单数据失败", + "loadOrderSuccess": "已加载原订单数据", + "saveFailed": "保存失败", + "submitFailed": "提交失败", + "fileUploadSuccess": "文件上传成功!", + "fileUploadFailed": "文件上传失败!", + "fileDeleted": "文件已删除", + "uploading": "上传中...", + "submitting": "提交中...", + "atLeastOneItem": "至少保留一条明细", + "atLeastOnePayment": "至少保留一条收款信息", + "fees": { + "handlingFee": "手续费", + "retrofitFee": "改装费", + "fobTransportFee": "FOB运费", + "portMiscFee": "港杂费", + "oceanFreight": "海运费", + "domesticFreight": "国内运费", + "commission": "佣金", + "purchaseTax": "购置税", + "taxRefund": "退税", + "accessoryPrice": "配件价", + "retentionPrice": "挂账", + "carPrice": "车价", + "increasePrice": "加价", + "clearancePrice": "报关价", + "profit": "利润", + "officialPrice": "官方指导价", + "discount": "优惠额", + "listingPrice": "挂牌价", + "reduction": "二手减价", + "totalPrice": "总价" + }, + "payment": { + "remitter": "汇款人", + "remitCountry": "汇款国", + "remitDate": "汇款日期", + "account": "收款账户", + "amount": "金额", + "paymentItem": "收款{{num}}" + }, + "summary": { + "summaryInfo": "汇总信息", + "profit": "利润" + }, + "searchPlaceholder": "搜索客户名称", + "fileUpload": { + "contract": "上传合同", + "piDoc": "上传PI", + "transactionSlip": "上传水单", + "clickToUpload": "点击上传", + "supportMultiple": "支持多文件上传", + "preview": "预览", + "download": "下载", + "delete": "删除", + "fileTypeAccept": "只能上传{{accept}}格式的文件!", + "fileSizeMax": "文件大小不能超过 {{maxSize}}MB!" + }, + "validation": { + "enterPiNo": "请输入阿里订单号/PI号", + "selectDate": "请选择日期", + "selectClearanceCountry": "请选择清关目的国", + "selectCustomer": "请选择客户", + "enterDeclarant": "请输入清关人", + "selectEtaShipDate": "请选择预计发送日", + "selectDomesticPort": "请选择国内收货港", + "selectShippingMethod": "请选择运输方式", + "selectTradeTerm": "请选择贸易条款", + "selectClearancePort": "请选择清关目的港", + "selectLatestArrival": "请选择最迟到港时间", + "selectAllVehicles": "请选择所有车型的车辆", + "uploadContract": "请上传合同文件", + "uploadPiDoc": "请上传PI文件", + "uploadTransactionSlip": "请上传水单文件", + "selectPickUpDate": "请选择提车日期", + "selectCollectAllDate": "请选择全部收齐日期" + }, + "placeholder": { + "autoCalculate": "自动计算", + "enterPiNo": "请输入阿里订单号/PI号", + "selectDate": "请选择日期", + "selectClearanceCountry": "请选择清关目的国", + "selectCustomer": "请选择客户", + "autoFillCustomerSource": "选择客户后自动填充", + "autoFillCustomerCountry": "选择客户后自动填充", + "selectVehicle": "请选择车型", + "selectExteriorColor": "请选择外饰颜色", + "selectInteriorColor": "请选择内饰颜色", + "newCarMileage": "新车为0", + "enterVin": "请输入车架号", + "enterOfficialPrice": "请输入官方指导价", + "enterListingPrice": "请输入二手挂牌价", + "enterDiscount": "请输入优惠额", + "enterAmount": "请输入", + "enterIncreasePrice": "请输入加价", + "enterUnitPrice": "请输入收款价", + "calculatingClearancePrice": "正在计算报关价", + "calculatingCarPrice": "正在计算车价", + "calculatingTaxRefund": "正在计算预估退税额", + "calculatingProfit": "正在计算利润", + "enterRemark": "请输入备注", + "enterHandlingFee": "请输入", + "enterContractRate": "请输入合同汇率", + "enterDeclarant": "请输入清关人", + "selectDomesticPort": "请选择国内收货港", + "selectShippingMethod": "请选择运输方式", + "selectTradeTerm": "请选择贸易条款", + "selectClearancePort": "请选择清关目的港", + "enterRemitter": "请输入汇款人", + "selectRemitCountry": "请选择汇款国", + "selectAccount": "请选择账户", + "enterAmount": "请输入金额", + "searchCustomer": "搜索客户", + "searchVehicle": "搜索车型", + "selectPickUpDate": "请选择提车日期", + "selectCollectAllDate": "请选择全部收齐日期", + "enterMileage": "请输入里程" + }, + "entry": { + "edit": "编辑备货单", + "copy": "复制备货单", + "title": "备货录入", + "baseInfo": "基础信息", + "baseInfoReadOnly": "基础信息(只读)", + "orderDetail": "订单明细", + "transportInfo": "运输详情", + "paymentInfo": "计划收款", + "fileUpload": "文件上传", + "saveDraft": "保存草稿", + "submitApproval": "提交审批", + "createFinal": "创建终版", + "detail": "详情", + "totalPrice": "合计", + "unitPrice": "单价", + "carPrice": "车价", + "profit": "利润", + "officialPrice": "官方指导价", + "discount": "优惠额", + "listingPrice": "挂牌价", + "reduction": "二手减价", + "add": "添加", + "copy": "复制" + }, + "select": { + "customer": "选择客户", + "vehicle": "选择车型", + "year": "年款", + "msrp": "指导价" + } } } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 7d84477..9978701 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -27,9 +27,7 @@ function App() { } createRoot(document.getElementById('root')!).render( - - - - - + + + ) diff --git a/frontend/src/pages/approval/list.tsx b/frontend/src/pages/approval/list.tsx index 25af7d3..fb607da 100644 --- a/frontend/src/pages/approval/list.tsx +++ b/frontend/src/pages/approval/list.tsx @@ -67,16 +67,6 @@ export default function ApprovalList() { const currentTab = tab || activeTab - console.log('🔍 Loading approval list START:', { - requestId, - currentTab, - activeTab, - tab, - pageNum, - isRefresh, - requestIdRefNow: requestIdRef.current - }) - setLoading(true) try { @@ -86,31 +76,19 @@ export default function ApprovalList() { page_size: 20, }) - console.log('📦 API response received:', { - requestId, - currentTab, - requestIdRefNow: requestIdRef.current, - count: result?.list?.length - }) - // 检查是否是最新的请求 if (requestIdRef.current !== requestId) { - console.log('⏭️ Skipping outdated response:', { - requestId, - latestRequestId: requestIdRef.current - }) return } const newList = result?.list || [] // 直接更新数据 - console.log('✅ Updating list for tab:', currentTab, 'items:', newList.length) setList(newList) setHasMore(newList.length >= 20) setPage(pageNum) } catch (error: any) { - console.error('❌ Failed to load approvals:', error) + // 静默处理错误 } finally { setLoading(false) } @@ -118,7 +96,6 @@ export default function ApprovalList() { // 切换标签 const handleTabChange = (key: string) => { - console.log('🔄 Tab changing from', activeTab, 'to', key) setActiveTab(key as 'my' | 'pending' | 'approved' | 'cc') // 保存 tab 到 sessionStorage sessionStorage.setItem('approval-tab', key) @@ -141,12 +118,7 @@ export default function ApprovalList() { // 初始加载 useEffect(() => { - console.log('🚀 Initial load effect') loadData(1, true) - - return () => { - console.log('🧹 Cleanup function called') - } }, []) // 查看详情 diff --git a/frontend/src/pages/dashboard/index.tsx b/frontend/src/pages/dashboard/index.tsx index fba237f..61f55f2 100644 --- a/frontend/src/pages/dashboard/index.tsx +++ b/frontend/src/pages/dashboard/index.tsx @@ -33,8 +33,10 @@ interface ActivityItem { // 从应用页面迁移的完整应用列表 const quickActions: QuickAction[] = [ { id: '1', icon: '✈️', label: '出差申请', path: '/business-trip/apply', color: '#1677ff', description: '提交出差申请' }, - { id: '2', icon: '📝', label: '采购申请', path: '/purchase/apply', color: '#52c41a', description: '物品采购审批', disabled: true }, - { id: '3', icon: '📦', label: '备货录入', path: '/stock/entry', color: '#faad14', description: '库存备货录入', disabled: true }, + { id: '2', icon: '📦', label: '备货单', path: '/stock/order', color: '#52c41a', description: '备货单管理' }, + { id: '3', icon: '📋', label: '终版备货单', path: '/stock/final', color: '#722ed1', description: '终版备货单管理' }, + { id: '4', icon: '📝', label: '采购申请', path: '/purchase/apply', color: '#13c2c2', description: '物品采购审批', disabled: true }, + { id: '5', icon: '📥', label: '备货录入', path: '/stock/entry', color: '#faad14', description: '库存备货录入' }, ] const allApps: QuickAction[] = [ diff --git a/frontend/src/pages/login-simple/index.tsx b/frontend/src/pages/login-simple/index.tsx index aea3ba6..0017a4a 100644 --- a/frontend/src/pages/login-simple/index.tsx +++ b/frontend/src/pages/login-simple/index.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { Button, Input, Toast } from 'antd-mobile' import { useUserStore } from '@/stores/user' +import { fdeskApiObj } from '@/services/api' import styles from './login-simple.module.css' export default function SimpleLogin() { @@ -30,9 +31,35 @@ export default function SimpleLogin() { const result = await response.json() if (result.code === 0) { + const { user, token } = result.data // 登录成功,保存到 store - login(result.data.user, result.data.token) + login(user, token) localStorage.setItem('i18nextLng', 'zh') + + // 获取用户系统映射,然后获取 fdesk 系统的独立 token + if (user.company?.abbr) { + fdeskApiObj.getSystemMapping() + .then((mappingResult: any) => { + // api 响应拦截器返回 data.data,所以直接访问 system_user_id + if (mappingResult?.system_user_id) { + return fdeskApiObj.getSystemToken({ + system_user_id: mappingResult.system_user_id, + corp_abbr: user.company.abbr, + }) + } + return Promise.resolve(null) + }) + .then((fdeskResult: any) => { + if (fdeskResult?.access_token) { + const { setFdeskToken } = useUserStore.getState() + setFdeskToken(fdeskResult.access_token) + } + }) + .catch(() => { + // 不阻塞登录流程,继续执行 + }) + } + Toast.show('登录成功') // 跳转到首页 navigate('/dashboard') diff --git a/frontend/src/pages/login/QRCodeLogin.tsx b/frontend/src/pages/login/QRCodeLogin.tsx index ec9a1c2..8611e32 100644 --- a/frontend/src/pages/login/QRCodeLogin.tsx +++ b/frontend/src/pages/login/QRCodeLogin.tsx @@ -2,6 +2,8 @@ import { useState, useEffect, useRef } from 'react' import { Dialog, Toast } from 'antd-mobile' import { QRCodeSVG } from 'qrcode.react' import { useNavigate } from 'react-router-dom' +import { useUserStore } from '@/stores/user' +import { fdeskApiObj } from '@/services/api' import styles from './QRCodeLogin.module.css' // 声明飞书 QR SDK 类型 @@ -247,25 +249,46 @@ export default function QRCodeLogin({ visible, onClose }: QRCodeLoginProps) { if (result.code === 0) { if (result.data.status === 'success') { - console.log('✅ Login success detected, navigating to dashboard...') setStatus('success') // 登录成功,保存 token const { token, user } = result.data localStorage.setItem('token', token) localStorage.setItem('i18nextLng', 'zh') - // 触发登录成功事件,让 store 更新 - window.dispatchEvent(new CustomEvent('feishu-login-success', { detail: { user, token } })) + // 更新 store + const { login, setFdeskToken } = useUserStore.getState() + login(user, token) + + // 获取用户系统映射,然后获取 fdesk 系统的独立 token + if (user.company?.abbr) { + fdeskApiObj.getSystemMapping() + .then((mappingResult: any) => { + // api 响应拦截器返回 data.data,所以直接访问 system_user_id + if (mappingResult?.system_user_id) { + return fdeskApiObj.getSystemToken({ + system_user_id: mappingResult.system_user_id, + corp_abbr: user.company.abbr, + }) + } + return Promise.resolve(null) + }) + .then((fdeskResult: any) => { + if (fdeskResult?.access_token) { + setFdeskToken(fdeskResult.access_token) + } + }) + .catch(() => { + // 不阻塞登录流程,继续执行 + }) + } Toast.show('登录成功') setTimeout(() => { - console.log('🚀 Navigating...') onClose() // 检查是否有 redirect 参数 const urlParams = new URLSearchParams(window.location.search) const redirect = urlParams.get('redirect') if (redirect) { - console.log('🔗 登录成功,跳转到:', redirect) window.location.href = redirect } else { navigate('/dashboard') diff --git a/frontend/src/pages/login/index.tsx b/frontend/src/pages/login/index.tsx index f84827d..9401ab3 100644 --- a/frontend/src/pages/login/index.tsx +++ b/frontend/src/pages/login/index.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Toast } from 'antd-mobile' import { useUserStore } from '@/stores/user' +import { fdeskApiObj } from '@/services/api' import QRCodeLogin from './QRCodeLogin' import styles from './login.module.css' @@ -219,7 +220,33 @@ export default function Login() { const result = await response.json() if (result.code === 0) { - login(result.data.user, result.data.token) + const { user, token } = result.data + login(user, token) + + // 获取用户系统映射,然后获取 fdesk 系统的独立 token + if (user.company?.abbr) { + fdeskApiObj.getSystemMapping() + .then((mappingResult: any) => { + // api 响应拦截器返回 data.data,所以直接访问 system_user_id + if (mappingResult?.system_user_id) { + return fdeskApiObj.getSystemToken({ + system_user_id: mappingResult.system_user_id, + corp_abbr: user.company.abbr, + }) + } + return Promise.resolve(null) + }) + .then((fdeskResult: any) => { + if (fdeskResult?.access_token) { + const { setFdeskToken } = useUserStore.getState() + setFdeskToken(fdeskResult.access_token) + } + }) + .catch(() => { + // 不阻塞登录流程,继续执行 + }) + } + Toast.show('登录成功') const urlParams = new URLSearchParams(window.location.search) diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index db03efe..a436cc1 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -17,6 +17,14 @@ import Login from '@/pages/login' import SimpleLogin from '@/pages/login-simple' import DebugFeishu from '@/pages/debug-feishu' import FeishuEnvTest from '@/pages/feishu-env-test' +import StockEntry from '@/pages/stock/entry' +import StockOrder from '@/pages/stock/order' +import StockShow from '@/pages/stock/show' +import StockEdit from '@/pages/stock/edit' +import StockFinal from '@/pages/stock/final' +import StockFinalShow from '@/pages/stock/final-show' +import StockFinalEdit from '@/pages/stock/final-edit' +import StockFinalEntry from '@/pages/stock/final-entry' const router = createBrowserRouter([ { @@ -36,6 +44,14 @@ const router = createBrowserRouter([ { path: 'business-trip/:id', element: }, { path: 'approval', element: }, { path: 'approval/:id', element: }, + { path: 'stock/entry', element: }, + { path: 'stock/order', element: }, + { path: 'stock/show', element: }, + { path: 'stock/edit', element: }, + { path: 'stock/final', element: }, + { path: 'stock/final-show', element: }, + { path: 'stock/final-edit', element: }, + { path: 'stock/final-entry', element: }, ], }, // 详情页移到顶层,支持未登录访问(页面自己处理登录检查) diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index ad361b6..85ab4aa 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -1,5 +1,6 @@ import axios from 'axios' import { Toast } from 'antd-mobile' +import HmacMD5 from 'crypto-js/hmac-md5' // 声明飞书 JSAPI 类型 declare global { @@ -42,6 +43,9 @@ export interface LoginResponse { const API_BASE = '/api/v1' +// ========== Fdesk API 配置 ========== +const FDESK_BASE = 'https://fdesk.hnxinyouhe.com/' + // 标记是否正在刷新 token let isRefreshing = false let failedQueue: Array<{ @@ -240,6 +244,89 @@ const api = axios.create({ }, }) +// 创建 fdesk axios 实例(独立的一套请求系统) +const fdeskApi = axios.create({ + baseURL: FDESK_BASE, + timeout: 30000, + headers: { + 'Content-Type': 'application/json', + }, +}) + +// 获取公司标识的辅助函数 +// zustand persist 存储结构: { state: { user, currentCompany, token, isLoggedIn }, version: 0 } +const getCompanyAbbr = (): string | undefined => { + try { + const userStorage = localStorage.getItem('user-storage') + if (userStorage) { + const stored = JSON.parse(userStorage) + // zustand persist 存储在 state 对象下 + const abbr = stored.state?.currentCompany?.abbr + console.log('🔍 [getCompanyAbbr] abbr:', abbr) + return abbr + } + } catch (e) { + console.error('❌ [getCompanyAbbr] 获取公司标识失败:', e) + } + return undefined +} + +// fdesk 请求拦截器:添加特殊请求头 +fdeskApi.interceptors.request.use( + (config) => { + // 使用独立的 fdesk_token(不是本地登录的 token) + const fdeskToken = localStorage.getItem('fdesk_token') + if (fdeskToken) { + config.headers['Fdesk-Auth-Token'] = `Bearer ${fdeskToken}` + } + // 添加公司标识 + const companyAbbr = getCompanyAbbr() + if (companyAbbr) { + config.headers['Fdesk-Corp-Abbr'] = companyAbbr + } + // 添加语言头 + const lang = localStorage.getItem('i18nextLng') || 'zh' + config.headers['Fdesk-Language'] = lang + // 固定头 + config.headers['Fdesk-Login-Type'] = 'feishu' + config.headers['Fdesk-System-Code'] = 'fdesk' + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// fdesk 响应拦截器:处理错误 +// 参考项目使用 code: 200 表示成功,同时兼容 code: 0 和直接返回数据的情况 +fdeskApi.interceptors.response.use( + (response) => { + const { data } = response + + // 情况1: 标准 fdesk 响应 { code: 200, data: {...} } + if (data.code === 200) { + return data.data !== undefined ? data.data : data + } + + // 情况2: 兼容 code: 0 的响应 + if (data.code === 0 || data.success === true) { + return data.data !== undefined ? data.data : data + } + + // 情况3: 直接返回数据(没有 code 字段),如数组等 + if (data.code === undefined) { + return data + } + + // 错误响应 + return Promise.reject(new Error(data.message || data.msg || '请求失败')) + }, + async (error) => { + const message = error.response?.data?.message || error.response?.data?.msg || error.message || '网络错误' + return Promise.reject(new Error(message)) + } +) + // 请求拦截器:添加 token 和语言 api.interceptors.request.use( (config) => { @@ -401,4 +488,129 @@ export const approvalApi = { cancel: (id: number) => api.post(`/approval/cancel/${id}`), } +// ========== Fdesk API ========== +// Fdesk API 接口(使用独立的请求头和 baseURL) +export const fdeskApiObj = { + // 示例:获取用户信息 + getUserInfo: () => fdeskApi.get('/api/user/info'), + + // 示例:获取列表 + getList: (params?: any) => fdeskApi.get('/api/list', { params }), + + // 示例:创建数据 + create: (data: any) => fdeskApi.post('/api/create', data), + + // ========== 备货单 API ========== + // 备货单列表 + stockingList: (params?: any) => fdeskApi.get('/crm/stock/order/list', { params }), + + // 备货单详情 + stockingDetail: (id: string | number) => fdeskApi.get('/crm/stock/order/detail', { params: { id } }), + stockingShow: (id: string | number) => fdeskApi.get('/crm/stock/order/show', { params: { id } }), + checkStock: (data: { id: string | number }) => fdeskApi.get('/crm/stock/order/check', { params: data }), + + // 添加备货单 + stockingAdd: (data: unknown) => fdeskApi.post('/crm/stock/order/store', data), + + // 编辑备货单 + stockingUpdate: (data: any) => fdeskApi.post('/crm/stock/order/update', data), + + // 提交备货单审批 + stockingSubmit: (data: any) => fdeskApi.post('/crm/stock/order/approval', data), + + // 备货单打印 + stockingPrint: (id: string | number) => fdeskApi.get('/crm/stock/order/print', { params: { id } }), + + // 备货单审批记录 + stockingApprovalRecords: (params?: any) => fdeskApi.get('/crm/approval/records/todo', { params }), + + // 备货单审批通过/拒绝 + stockingApprovalAction: (data: any) => fdeskApi.post('/crm/approval/records/action', data), + + // 备货单审批历史 + stockingApprovalLogs: (params?: any) => fdeskApi.get('/crm/approval/records/logs', { params }), + + // 终极备货单提交 + stockingUltimateApproval: (data: any) => fdeskApi.post('/crm/stock/order/ultimate/approval', data), + + // 备货单撤回 + stockingRecall: (data: any) => fdeskApi.post('/crm/stock/order/revoke/approval', data), + + // 备货单作废 + stockingInvalid: (data: any) => fdeskApi.post('/crm/stock/order/nullify/approval', data), + + // 备货单删除 + stockingDelete: (data: any) => fdeskApi.post('/crm/stock/order/destroy', data), + + // 获取客户列表(分页) + getCustomerList: (params?: any) => fdeskApi.get('/crm/customer/list', { params }), + + // 获取国家列表 + getCountryList: () => fdeskApi.get('/crm/customer/getCountry'), + + // 获取渠道列表 + getChannelList: () => fdeskApi.get('/crm/customer/getChannel'), + + // 获取港口下拉选项(用于表单选择) + getPortSelect: (params?: any) => fdeskApi.get('/common/preset/port', { params }), + + // 获取车型列表(分页,用于选择车型) + getVehicleList: (params?: any) => fdeskApi.get('/crm/vehicle/sku/list', { params }), + + // 获取币种列表 + getCurrencyList: () => fdeskApi.get('/common/currency/list'), + + // 获取币种下拉选项(用于表单选择) + getCurrencySelect: () => fdeskApi.get('/common/preset/currency'), + + // 获取账户下拉选项(用于表单选择) + getAccountSelect: () => fdeskApi.get('/common/preset/account'), + + // 获取颜色下拉选项(type: 1=外饰, 2=内饰) + getColorSelect: (params: any) => fdeskApi.get('/common/preset/color', { params }), + + // 获取字典数据(运输方式、贸易条款等) + // scope: '2,15' -> 2=运输方式(transport), 15=贸易条款(trade_terms) + getDictSelect: (params?: any) => fdeskApi.get('/admin/dict/scope', { params }), + + // 获取运输方式列表(使用 getDictSelect({ scope: '2,15' }) 返回的 transport 字段) + getTransportList: () => fdeskApi.get('/admin/dict/scope', { params: { scope: '2,15' } }), + + // 获取贸易条款列表(使用 getDictSelect({ scope: '2,15' }) 返回的 trade_terms 字段) + getTradeTermList: () => fdeskApi.get('/admin/dict/scope', { params: { scope: '2,15' } }), + + // 上传文件 + uploadFile: (file: File, scene: string = 'stock_orders') => { + const formData = new FormData() + formData.append('file', file) + formData.append('scene', scene) + return fdeskApi.post('/ticket/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' } + }) + }, + + // 获取审批时间线(用于显示审批历史) + stockingApprovalTimeline: (params?: any) => fdeskApi.get('/admin/approval/records/timeline', { params }), + + // ========== Fdesk 系统 Token API ========== + // 获取用户系统映射(返回 system_user_id) + getSystemMapping: () => api.get('/user/system-mapping', { params: { system: 'fdesk' } }), + + // 获取 fdesk 系统的独立 token + getSystemToken: (params: { system_user_id: string; corp_abbr: string }) => { + const timestamp = Math.floor(Date.now() / 1000) + // 使用 HMAC-MD5 算法生成签名 + const signature = HmacMD5(`${params.system_user_id}${timestamp}`, 'a7H9kL2pQ5sW8xZ1').toString() + + return fdeskApi.post('/api/system/auth', { + user_id: params.system_user_id, + timestamp, + signature, + system_code: 'fdesk', + corp_abbr: params.corp_abbr, + }) + } +} + +export { fdeskApi } export default api diff --git a/frontend/src/stores/user.ts b/frontend/src/stores/user.ts index 28d482b..551e034 100644 --- a/frontend/src/stores/user.ts +++ b/frontend/src/stores/user.ts @@ -7,6 +7,7 @@ interface UserState { token: string | null currentCompany: Company | null isLoggedIn: boolean + fdeskToken: string | null // Fdesk 系统的独立 token // Actions setUser: (user: User | null) => void @@ -14,6 +15,7 @@ interface UserState { setCurrentCompany: (company: Company | null) => void login: (user: User, token: string) => void logout: () => void + setFdeskToken: (token: string | null) => void } export const useUserStore = create()( @@ -23,6 +25,7 @@ export const useUserStore = create()( token: null, currentCompany: null, isLoggedIn: false, + fdeskToken: null, setUser: (user) => set({ user }), setToken: (token) => set({ token }), @@ -30,12 +33,22 @@ export const useUserStore = create()( login: (user, token) => { localStorage.setItem('token', token) - set({ user, token, isLoggedIn: true }) + set({ user, token, isLoggedIn: true, currentCompany: user.company || null }) }, logout: () => { localStorage.removeItem('token') - set({ user: null, token: null, currentCompany: null, isLoggedIn: false }) + localStorage.removeItem('fdesk_token') + set({ user: null, token: null, currentCompany: null, isLoggedIn: false, fdeskToken: null }) + }, + + setFdeskToken: (token) => { + if (token) { + localStorage.setItem('fdesk_token', token) + } else { + localStorage.removeItem('fdesk_token') + } + set({ fdeskToken: token }) }, }), { @@ -45,6 +58,7 @@ export const useUserStore = create()( token: state.token, currentCompany: state.currentCompany, isLoggedIn: state.isLoggedIn, + fdeskToken: state.fdeskToken, }), } ) diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 4025ce8..19c29c4 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -24,6 +24,7 @@ export type ApprovalNodeType = (typeof ApprovalNodeType)[keyof typeof ApprovalNo export interface User { id: number company_id: number + company?: Company // 用户所属公司信息(登录时返回) sn?: string name: string email?: string @@ -38,7 +39,11 @@ export interface User { export interface Company { id: string name: string + abbr?: string // 公司标识,用于 fdesk API 请求头 Fdesk-Corp-Abbr + tenant_key?: string feishuAppId?: string + feishu_enabled?: boolean + sort?: number status: number }