3 Commits

Author SHA1 Message Date
浅念 1fff0a7563 添加 备货单模块和用户系统映射 2026-03-05 13:25:35 +08:00
18338781771 0da3969bd6 用户映射关系 2026-03-03 15:07:13 +08:00
18338781771 ae9b82e683 获取fdesk对应的用户id 2026-03-03 14:50:41 +08:00
20 changed files with 1964 additions and 50 deletions
+33
View File
@@ -3,6 +3,7 @@
namespace app\controller;
use app\model\User;
use app\model\UserSystemMapping;
use Webman\Http\Request;
use Webman\Http\Response;
@@ -130,4 +131,36 @@ class UserController extends BaseController
'page_size' => $pageSize,
]);
}
/**
* 获取用户在指定系统的映射ID
*/
public function systemMapping(Request $request): Response
{
$userId = $this->getUserId($request);
$systemCode = $request->get('system', 'fdesk');
// 获取用户信息以获取公司ID
$user = User::find($userId);
if (!$user) {
return $this->error('用户不存在');
}
// 查询系统映射
$mapping = UserSystemMapping::active()
->bySystem($systemCode)
->where('user_id', $userId)
->where('company_id', $user->company_id)
->first();
if (!$mapping) {
return $this->error('未找到系统映射信息,请联系管理员配置');
}
return $this->success([
'system_code' => $mapping->system_code,
'system_user_id' => $mapping->system_user_id,
'system_user_name' => $mapping->system_user_name,
]);
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace app\model;
use support\Model;
class UserSystemMapping extends Model
{
protected $table = 'user_system_mapping';
/**
* 可填充字段
*/
protected $fillable = [
'user_id',
'company_id',
'system_code',
'system_user_id',
'system_user_name',
'status',
];
protected $casts = [
'user_id' => 'integer',
'company_id' => 'integer',
'status' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* 关联用户
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* 关联公司
*/
public function company()
{
return $this->belongsTo(Company::class, 'company_id');
}
/**
* 获取有效的映射
*/
public function scopeActive($query)
{
return $query->where('status', 1);
}
/**
* 根据系统代码查找
*/
public function scopeBySystem($query, string $systemCode)
{
return $query->where('system_code', $systemCode);
}
/**
* 根据公司ID查找
*/
public function scopeByCompany($query, int $companyId)
{
return $query->where('company_id', $companyId);
}
/**
* 根据外部系统用户ID查找
*/
public function scopeBySystemUserId($query, string $systemUserId, string $systemCode = 'fdesk')
{
return $query->where('system_code', $systemCode)
->where('system_user_id', $systemUserId);
}
/**
* 获取用户在指定系统的映射
*/
public function scopeUserMapping($query, int $userId, int $companyId, string $systemCode = 'fdesk')
{
return $query->where('user_id', $userId)
->where('company_id', $companyId)
->where('system_code', $systemCode);
}
}
+1
View File
@@ -36,6 +36,7 @@ Route::group('/api/v1/user', function () {
Route::get('/menu', [app\controller\UserController::class, 'menu']);
Route::get('/companies', [app\controller\UserController::class, 'companies']);
Route::get('/list', [app\controller\UserController::class, 'list']);
Route::get('/system-mapping', [app\controller\UserController::class, 'systemMapping']);
Route::post('/switch-company', [app\controller\UserController::class, 'switchCompany']);
Route::post('/change-password', [app\controller\UserController::class, 'changePassword']);
})->middleware($authMiddleware);
+36
View File
@@ -900538,4 +900538,40 @@ INSERT INTO `users_copy1` VALUES (73, '833601', 1, '郭炯00078', 'https://s1-im
INSERT INTO `users_copy1` VALUES (74, '263364', 3, '李永春', 'https://s3-imfile.feishucdn.com/static-resource/v1/v3_00kb_ebbfbf17-c4b2-4306-8896-91ee3ef0b64g~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp', NULL, 'liyongchun@paikacar.com', '13073773542', '$2y$10$ed9m0Ecvj6FES2ntlRwedObN./Giv0.CmMZAvkKtA4dd2IB5PBbcm', 1, 1, 'on_bf01c7ea230962313781a54c927eba10', 2, 'x_server/user_signature/20260202/b9ce62914096acc13e12f16cc4740ef1_697ffc95e5338.png', '2026-01-29 09:50:17', '2026-02-26 16:18:51');
INSERT INTO `users_copy1` VALUES (75, '698484', 3, '曲江丽', 'https://s1-imfile.feishucdn.com/static-resource/v1/v3_00uh_6412ff58-77d6-4e5f-b35e-6b1a76ed31eg~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp', NULL, 'qujiangli@paikacar.com', '18339956922', '$2y$10$j9w0jkEYliIPbGbiPK5MSOhqGwwzY1Enf/koZC2MVb4JrXSFUhqDq', 1, 1, 'on_8f01d5bee7af07ce3de5e31df7075c99', 2, 'x_server/user_signature/20260210/6ad00820e9be180db7ac4aa729e4b6e9_698ad1464aae9.png', '2026-02-10 14:25:49', '2026-02-26 16:18:51');
-- ----------------------------
-- Table structure for user_system_mapping
-- ----------------------------
DROP TABLE IF EXISTS `user_system_mapping`;
CREATE TABLE `user_system_mapping` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` bigint UNSIGNED NOT NULL COMMENT '本项目用户ID',
`company_id` bigint UNSIGNED NOT NULL COMMENT '公司ID',
`system_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'fdesk' COMMENT '系统代码(如:fdesk, erp, crm)',
`system_user_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '外部系统用户ID',
`system_user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '外部系统用户名(冗余字段,便于查看)',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=有效,0=无效',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_user_company_system` (`user_id`, `company_id`, `system_code`) USING BTREE,
INDEX `idx_system_user_id`(`system_code`, `system_user_id`) USING BTREE,
INDEX `idx_company_id`(`company_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户系统映射表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_system_mapping
-- ----------------------------
INSERT INTO `user_system_mapping` VALUES (1, 1, 2, 'fdesk', '1', '李佳博', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (2, 2, 2, 'fdesk', '74', '陈兰', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (3, 3, 2, 'fdesk', '78', '陈标', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (4, 4, 1, 'fdesk', '185', '闫继光', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (5, 76, 1, 'fdesk', '175', '王爽', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (6, 78, 3, 'fdesk', '175', '王爽', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (7, 79, 2, 'fdesk', '5', '闫继光', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (8, 82, 2, 'fdesk', '75', '范彩凤', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (9, 85, 1, 'fdesk', '183', '李冰杰', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (10, 88, 1, 'fdesk', '71', '崔琪', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (11, 90, 3, 'fdesk', '205', '李永春', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (12, 92, 1, 'fdesk', '47', '朱海卫', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
SET FOREIGN_KEY_CHECKS = 1;
+35 -5
View File
@@ -1,8 +1,8 @@
# 数据库结构文档
**版本**: 1.1.0
**更新时间**: 2026-03-01
**状态**: 线上稳定版本
**版本**: 1.1.1
**更新时间**: 2026-03-03
**状态**: 开发中
---
@@ -358,6 +358,34 @@
---
### 15. user_system_mapping - 用户系统映射表
用于将本项目用户ID映射到外部系统(如fdesk、ERP、CRM等)的用户ID。
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | bigint unsigned | ✓ | AUTO_INCREMENT | 记录ID |
| user_id | bigint unsigned | ✓ | | 本项目用户ID |
| company_id | bigint unsigned | ✓ | | 公司ID |
| system_code | varchar(50) | ✓ | fdesk | 系统代码(如:fdesk, erp, crm) |
| system_user_id | varchar(100) | ✓ | | 外部系统用户ID |
| system_user_name | varchar(100) | | | 外部系统用户名(冗余字段) |
| status | tinyint | ✓ | 1 | 状态:1=有效,0=无效 |
| created_at | timestamp | ✓ | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | timestamp | ✓ | CURRENT_TIMESTAMP | 更新时间 |
**索引**:
- PRIMARY KEY (`id`)
- UNIQUE KEY `uk_user_company_system` (`user_id`, `company_id`, `system_code`)
- KEY `idx_system_user_id` (`system_code`, `system_user_id`)
- KEY `idx_company_id` (`company_id`)
**说明**:
- 同一用户在同一公司下对应一个外部系统只能有一条记录
- 支持多个外部系统的映射关系
---
## 📊 表关系图
```
@@ -368,6 +396,8 @@ users (1) ----< (N) user_feishu_apps
+----> companies (1) ----< (N) company_feishu_apps
|
+----> user_sn (N)
|
+----> user_system_mapping (N)
users (1) ----< (N) business_trip
| |
@@ -419,5 +449,5 @@ users (1) ----< (N) business_trip
---
**维护人**: 开发团队
**最后更新**: 2026-03-01
**版本**: 1.1.0 (线上稳定版)
**最后更新**: 2026-03-03
**版本**: 1.1.1
+289
View File
@@ -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"
}
}
}
+289
View File
@@ -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"
}
}
}
+289
View File
@@ -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"
}
}
}
+289
View File
@@ -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": "Рекомендуемая цена"
}
}
}
+273
View File
@@ -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": "指导价"
}
}
}
+3 -5
View File
@@ -27,9 +27,7 @@ function App() {
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
</StrictMode>
<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>
)
+1 -29
View File
@@ -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')
}
}, [])
// 查看详情
+4 -2
View File
@@ -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[] = [
+28 -1
View File
@@ -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')
+28 -5
View File
@@ -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')
+28 -1
View File
@@ -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)
+16
View File
@@ -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: <BusinessTripDetail /> },
{ path: 'approval', element: <ApprovalList /> },
{ path: 'approval/:id', element: <ApprovalDetail /> },
{ path: 'stock/entry', element: <StockEntry /> },
{ path: 'stock/order', element: <StockOrder /> },
{ path: 'stock/show', element: <StockShow /> },
{ path: 'stock/edit', element: <StockEdit /> },
{ path: 'stock/final', element: <StockFinal /> },
{ path: 'stock/final-show', element: <StockFinalShow /> },
{ path: 'stock/final-edit', element: <StockFinalEdit /> },
{ path: 'stock/final-entry', element: <StockFinalEntry /> },
],
},
// 详情页移到顶层,支持未登录访问(页面自己处理登录检查)
+212
View File
@@ -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<any, any>('/api/user/info'),
// 示例:获取列表
getList: (params?: any) => fdeskApi.get<any, any>('/api/list', { params }),
// 示例:创建数据
create: (data: any) => fdeskApi.post<any, any>('/api/create', data),
// ========== 备货单 API ==========
// 备货单列表
stockingList: (params?: any) => fdeskApi.get<any, any>('/crm/stock/order/list', { params }),
// 备货单详情
stockingDetail: (id: string | number) => fdeskApi.get<any, any>('/crm/stock/order/detail', { params: { id } }),
stockingShow: (id: string | number) => fdeskApi.get<any, any>('/crm/stock/order/show', { params: { id } }),
checkStock: (data: { id: string | number }) => fdeskApi.get<any, any>('/crm/stock/order/check', { params: data }),
// 添加备货单
stockingAdd: (data: unknown) => fdeskApi.post<any, any>('/crm/stock/order/store', data),
// 编辑备货单
stockingUpdate: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/update', data),
// 提交备货单审批
stockingSubmit: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/approval', data),
// 备货单打印
stockingPrint: (id: string | number) => fdeskApi.get<any, any>('/crm/stock/order/print', { params: { id } }),
// 备货单审批记录
stockingApprovalRecords: (params?: any) => fdeskApi.get<any, any>('/crm/approval/records/todo', { params }),
// 备货单审批通过/拒绝
stockingApprovalAction: (data: any) => fdeskApi.post<any, any>('/crm/approval/records/action', data),
// 备货单审批历史
stockingApprovalLogs: (params?: any) => fdeskApi.get<any, any>('/crm/approval/records/logs', { params }),
// 终极备货单提交
stockingUltimateApproval: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/ultimate/approval', data),
// 备货单撤回
stockingRecall: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/revoke/approval', data),
// 备货单作废
stockingInvalid: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/nullify/approval', data),
// 备货单删除
stockingDelete: (data: any) => fdeskApi.post<any, any>('/crm/stock/order/destroy', data),
// 获取客户列表(分页)
getCustomerList: (params?: any) => fdeskApi.get<any, any>('/crm/customer/list', { params }),
// 获取国家列表
getCountryList: () => fdeskApi.get<any, any>('/crm/customer/getCountry'),
// 获取渠道列表
getChannelList: () => fdeskApi.get<any, any>('/crm/customer/getChannel'),
// 获取港口下拉选项(用于表单选择)
getPortSelect: (params?: any) => fdeskApi.get<any, any>('/common/preset/port', { params }),
// 获取车型列表(分页,用于选择车型)
getVehicleList: (params?: any) => fdeskApi.get<any, any>('/crm/vehicle/sku/list', { params }),
// 获取币种列表
getCurrencyList: () => fdeskApi.get<any, any>('/common/currency/list'),
// 获取币种下拉选项(用于表单选择)
getCurrencySelect: () => fdeskApi.get<any, any>('/common/preset/currency'),
// 获取账户下拉选项(用于表单选择)
getAccountSelect: () => fdeskApi.get<any, any>('/common/preset/account'),
// 获取颜色下拉选项(type: 1=外饰, 2=内饰)
getColorSelect: (params: any) => fdeskApi.get<any, any>('/common/preset/color', { params }),
// 获取字典数据(运输方式、贸易条款等)
// scope: '2,15' -> 2=运输方式(transport), 15=贸易条款(trade_terms)
getDictSelect: (params?: any) => fdeskApi.get<any, any>('/admin/dict/scope', { params }),
// 获取运输方式列表(使用 getDictSelect({ scope: '2,15' }) 返回的 transport 字段)
getTransportList: () => fdeskApi.get<any, any>('/admin/dict/scope', { params: { scope: '2,15' } }),
// 获取贸易条款列表(使用 getDictSelect({ scope: '2,15' }) 返回的 trade_terms 字段)
getTradeTermList: () => fdeskApi.get<any, any>('/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<any, any>('/ticket/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
},
// 获取审批时间线(用于显示审批历史)
stockingApprovalTimeline: (params?: any) => fdeskApi.get<any, any>('/admin/approval/records/timeline', { params }),
// ========== Fdesk 系统 Token API ==========
// 获取用户系统映射(返回 system_user_id)
getSystemMapping: () => api.get<any, any>('/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<any, any>('/api/system/auth', {
user_id: params.system_user_id,
timestamp,
signature,
system_code: 'fdesk',
corp_abbr: params.corp_abbr,
})
}
}
export { fdeskApi }
export default api
+16 -2
View File
@@ -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<UserState>()(
@@ -23,6 +25,7 @@ export const useUserStore = create<UserState>()(
token: null,
currentCompany: null,
isLoggedIn: false,
fdeskToken: null,
setUser: (user) => set({ user }),
setToken: (token) => set({ token }),
@@ -30,12 +33,22 @@ export const useUserStore = create<UserState>()(
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<UserState>()(
token: state.token,
currentCompany: state.currentCompany,
isLoggedIn: state.isLoggedIn,
fdeskToken: state.fdeskToken,
}),
}
)
+5
View File
@@ -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
}