Bläddra i källkod

fix: 完全修复前端 TypeScript 错误 (119→0)

docker 2 månader sedan
förälder
incheckning
7464016592
28 ändrade filer med 256 tillägg och 150 borttagningar
  1. 50 15
      frontend/src/api/services.ts
  2. 117 38
      frontend/src/types/page.ts
  3. 1 1
      frontend/src/views/auth/LoginView.vue
  4. 5 5
      frontend/src/views/channel/ChannelConfigView.vue
  5. 2 2
      frontend/src/views/crm/AutoReplyRuleView.vue
  6. 1 1
      frontend/src/views/crm/ServicePerformanceView.vue
  7. 1 1
      frontend/src/views/finance/PaymentView.vue
  8. 2 2
      frontend/src/views/finance/RefundView.vue
  9. 3 3
      frontend/src/views/inventory/InventoryOverviewView.vue
  10. 3 3
      frontend/src/views/inventory/ShippingWorkView.vue
  11. 14 14
      frontend/src/views/order/OrderAfterSaleView.vue
  12. 6 5
      frontend/src/views/order/OrderDetailView.vue
  13. 1 1
      frontend/src/views/order/OrderListView.vue
  14. 2 2
      frontend/src/views/product/ProductEditorView.vue
  15. 4 4
      frontend/src/views/product/ProductListView.vue
  16. 6 6
      frontend/src/views/product/ProductMappingView.vue
  17. 3 3
      frontend/src/views/product/ProductPricingView.vue
  18. 2 2
      frontend/src/views/report/CustomerAnalysisView.vue
  19. 1 1
      frontend/src/views/report/ProcurementReportView.vue
  20. 4 3
      frontend/src/views/report/ReportCenterView.vue
  21. 7 7
      frontend/src/views/supplier/PurchaseOrderView.vue
  22. 2 2
      frontend/src/views/supplier/SupplierListView.vue
  23. 5 5
      frontend/src/views/supplier/SupplyCapabilityView.vue
  24. 3 3
      frontend/src/views/system/ApiKeyView.vue
  25. 1 1
      frontend/src/views/system/DepartmentView.vue
  26. 3 1
      frontend/src/views/system/MessageTemplateView.vue
  27. 2 2
      frontend/src/views/system/RolePermissionView.vue
  28. 5 17
      frontend/src/views/warehouse/WarehouseView.vue

+ 50 - 15
frontend/src/api/services.ts

@@ -5,6 +5,7 @@ import type {
   ApprovalFlowItem,
   AutoReplyRule,
   ChatChannel,
+  ChatLogItem,
   ChatMessage,
   ChatSession,
   ChannelItem,
@@ -249,6 +250,7 @@ export const api = {
     request<void>(`/api/supplier/suppliers/${id}`, { method: 'DELETE' }),
 
   /* Supply Capabilities */
+  getSupplyCapabilities: () => request<{ items: SupplyCapabilityItem[] }>('/api/supplier/capabilities'),
   getSupplyCapabilitiesBySupplier: (supplierId: number) => request<{ items: SupplyCapabilityItem[] }>(`/api/supplier/${supplierId}/capabilities`),
   getSupplyCapabilitiesBySku: (skuId: number) => request<{ items: SupplyCapabilityItem[] }>(`/api/supplier/sku/${skuId}/capabilities`),
   getSupplyCapability: (id: number) => request<SupplyCapabilityItem>(`/api/supplier/capability/${id}`),
@@ -684,10 +686,15 @@ export const api = {
   testAutoReplyRuleLegacy: (id: string, testMessage: string) =>
     request<{ response: string }>(`/api/crm/auto-reply/rules/${id}/test`, { method: 'POST', body: JSON.stringify({ message: testMessage }) }),
 
-  /* CRM: Chat Log (legacy) */
-  getChatLogsLegacy: () => request<{ items: ChatSession[] }>('/api/crm/chat-logs'),
-  getChatLogDetailLegacy: (id: string) => request<ChatSession>(`/api/crm/chat-logs/${id}`),
-  markChatLogLegacy: (id: string, tag: string) =>
+  /* CRM: Chat Log */
+  getChatLogs: () => request<{ items: ChatLogItem[] }>('/api/crm/chat-logs'),
+  getChatLog: (id: number) => request<ChatLogItem>(`/api/crm/chat-logs/${id}`),
+  createChatLog: (data: Partial<ChatLogItem>) =>
+    request<ChatLogItem>('/api/crm/chat-logs', { method: 'POST', body: JSON.stringify(data) }),
+  updateChatLog: (id: number, data: Partial<ChatLogItem>) =>
+    request<ChatLogItem>(`/api/crm/chat-logs/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteChatLog: (id: number) => request<void>(`/api/crm/chat-logs/${id}`, { method: 'DELETE' }),
+  markChatLog: (id: number, tag: string) =>
     request<{ success: boolean }>(`/api/crm/chat-logs/${id}/mark`, { method: 'POST', body: JSON.stringify({ tag }) }),
 
   /* CRM: Service Performance (legacy) */
@@ -702,28 +709,56 @@ export const api = {
     request<ChatChannel>(`/api/crm/channels/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
   deleteChatChannelLegacy: (id: string) => request<{ success: boolean }>(`/api/crm/channels/${id}`, { method: 'DELETE' }),
 
-  /* Reports (legacy) */
+  /* Reports */
   getReports: () => request<{ items: ReportItem[] }>('/api/reports'),
-  getReportData: () => request<{ items: ReportDataItem[] }>('/api/report-data'),
-
-  /* Pricing Rules (legacy - path unclear) */
+  getReport: (id: number) => request<ReportItem>(`/api/reports/${id}`),
+  createReport: (data: Partial<ReportItem>) =>
+    request<ReportItem>('/api/reports', { method: 'POST', body: JSON.stringify(data) }),
+  updateReport: (id: number, data: Partial<ReportItem>) =>
+    request<ReportItem>(`/api/reports/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteReport: (id: number) => request<void>(`/api/reports/${id}`, { method: 'DELETE' }),
+  generateReport: (id: number) => request<ReportItem>(`/api/reports/${id}/generate`, { method: 'POST' }),
+
+  getReportData: (reportId?: number) =>
+    reportId ? request<{ items: ReportDataItem[] }>(`/api/report-data?reportId=${reportId}`) : request<{ items: ReportDataItem[] }>('/api/report-data'),
+  getReportDataById: (id: number) => request<ReportDataItem>(`/api/report-data/${id}`),
+  createReportData: (data: Partial<ReportDataItem>) =>
+    request<ReportDataItem>('/api/report-data', { method: 'POST', body: JSON.stringify(data) }),
+  updateReportData: (id: number, data: Partial<ReportDataItem>) =>
+    request<ReportDataItem>(`/api/report-data/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteReportData: (id: number) => request<void>(`/api/report-data/${id}`, { method: 'DELETE' }),
+
+  /* Pricing Rules */
   getPricingRules: () => request<{ items: PricingRuleItem[] }>('/api/pricing-rules'),
-  createPricingRule: (data: any) =>
+  getPricingRule: (id: number) => request<PricingRuleItem>(`/api/pricing-rules/${id}`),
+  createPricingRule: (data: Partial<PricingRuleItem>) =>
     request<PricingRuleItem>('/api/pricing-rules', { method: 'POST', body: JSON.stringify(data) }),
-  updatePricingRule: (id: string, data: any) =>
+  updatePricingRule: (id: number, data: Partial<PricingRuleItem>) =>
     request<PricingRuleItem>(`/api/pricing-rules/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deletePricingRule: (id: number) => request<void>(`/api/pricing-rules/${id}`, { method: 'DELETE' }),
 
-  /* CRM: Ticket (legacy) */
+  /* CRM: Ticket */
   getTickets: () => request<{ items: TicketItem[] }>('/api/crm/tickets'),
-  createTicket: (data: any) =>
+  getTicket: (id: number) => request<TicketItem>(`/api/crm/tickets/${id}`),
+  createTicket: (data: Partial<TicketItem>) =>
     request<TicketItem>('/api/crm/tickets', { method: 'POST', body: JSON.stringify(data) }),
-  updateTicket: (id: string, data: any) =>
+  updateTicket: (id: number, data: Partial<TicketItem>) =>
     request<TicketItem>(`/api/crm/tickets/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  assignTicket: (id: number, assignedTo: number, assignedName: string) =>
+    request<TicketItem>(`/api/crm/tickets/${id}/assign`, { method: 'POST', body: JSON.stringify({ assignedTo, assignedName }) }),
+  resolveTicket: (id: number, resolution: string) =>
+    request<TicketItem>(`/api/crm/tickets/${id}/resolve`, { method: 'POST', body: JSON.stringify({ resolution }) }),
+  deleteTicket: (id: number) => request<void>(`/api/crm/tickets/${id}`, { method: 'DELETE' }),
 
-  /* CRM: Satisfaction (legacy) */
+  /* CRM: Satisfaction */
   getSatisfactions: () => request<{ items: SatisfactionItem[] }>('/api/crm/satisfactions'),
-  updateSatisfaction: (id: string, data: any) =>
+  getSatisfaction: (id: number) => request<SatisfactionItem>(`/api/crm/satisfactions/${id}`),
+  createSatisfaction: (data: Partial<SatisfactionItem>) =>
+    request<SatisfactionItem>('/api/crm/satisfactions', { method: 'POST', body: JSON.stringify(data) }),
+  updateSatisfaction: (id: number, data: Partial<SatisfactionItem>) =>
     request<SatisfactionItem>(`/api/crm/satisfactions/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteSatisfaction: (id: number) => request<void>(`/api/crm/satisfactions/${id}`, { method: 'DELETE' }),
+  getSatisfactionAverageScore: () => request<{ averageScore: number }>('/api/crm/satisfactions/average-score'),
 
   /* System: Logs (legacy) */
   getLogs: () => request<{ items: LogItem[] }>('/api/system/operation-logs')

+ 117 - 38
frontend/src/types/page.ts

@@ -109,18 +109,36 @@ export interface MappingFormData {
 
 /* ───── Pricing ───── */
 export interface PricingRuleItem {
-  id: string;
-  sku: string;
-  productTitle: string;
-  currency: string;
-  basePrice: string;
-  channelPrice: string;
-  discountPrice: string;
-  effectiveTime: string;
-  safeStock: number;
+  id: number;
+  ruleNo: string;
+  ruleName: string;
+  ruleType: string;
+  productSkuId: string;
+  channelId: string;
+  basePrice: number;
+  discountRate: number;
+  fixedPrice: number;
+  priority: number;
   status: string;
-  modifiedBy: string;
+  startDate: string;
+  endDate: string;
+  createdBy: string;
+  createdAt: string;
+  updatedBy: string;
   updatedAt: string;
+  // Legacy fields for backward compatibility with views
+  sku?: string;
+  currency?: string;
+  discountPrice?: string;
+  channelPrice?: string;
+  safeStock?: number;
+  effectiveTime?: string;
+  expireTime?: string;
+  productTitle?: string;
+  modifiedBy?: string;
+  channelAdjustRule?: string;
+  safeStockThreshold?: number;
+  warningRecipients?: string;
 }
 
 export interface PricingRuleFormData {
@@ -270,6 +288,8 @@ export interface OrderProductItem {
   shippedQty: number;
   returnedQty: number;
   giftFlag: boolean;
+  mainQty?: number;
+  summary?: string;
 }
 
 export interface ProductSpec {
@@ -281,9 +301,10 @@ export interface StatusEvent {
   status: string;
   time: string;
   title: string;
-  summary: string;
+  summary?: string;
   type: string;
-  operator: string;
+  operator?: string;
+  operatorRole?: string;
 }
 
 export interface OperationLog {
@@ -458,19 +479,38 @@ export interface ShippingItem {
 
 /* ───── Report ───── */
 export interface ReportItem {
-  id: string;
+  id: number;
+  reportNo: string;
+  reportName: string;
   reportType: string;
-  dimensions: string;
-  owner: string;
+  periodType: string;
+  startDate: string;
+  endDate: string;
+  status: string;
+  createdBy: string;
+  createdAt: string;
+  updatedBy: string;
   updatedAt: string;
 }
 
 export interface ReportDataItem {
-  metric: string;
-  value: string;
-  mom: string;
-  yoy: string;
-  dimension: string;
+  id: number;
+  reportId: number;
+  dataKey: string;
+  dataValue: string;
+  dataType: string;
+  category: string;
+  subCategory: string;
+  numericValue: number;
+  stringValue: string;
+  reportDate: string;
+  dimensions: string;
+  createdAt: string;
+  // chart display fields (legacy)
+  metric?: string;
+  value?: string;
+  mom?: string;
+  yoy?: string;
 }
 
 /* ───── System ───── */
@@ -514,6 +554,7 @@ export interface ApiKeyItem {
 export type UserRole = 'admin' | 'customer_service' | 'finance' | 'manager' | 'operator' | 'procurement' | 'warehouse';
 
 export interface UserProfile {
+  username?: string;
   avatar: string;
   name: string;
   role: UserRole;
@@ -532,6 +573,8 @@ export interface WarehouseItem {
   status: string;
   manager: string;
   remark: string;
+  area?: number;
+  updatedAt?: string;
 }
 
 export interface ReturnPackageItem {
@@ -585,6 +628,7 @@ export interface PaymentItem {
   transactionNo: string;
   fee: string;
   remark: string;
+  orderId?: string | number;
 }
 
 export interface RefundItem {
@@ -598,6 +642,7 @@ export interface RefundItem {
   refundTime: string;
   refundStatus: string;
   reason: string;
+  orderId?: string | number;
 }
 
 export interface SupplierSettlementItem {
@@ -728,6 +773,9 @@ export interface OnlineVisitor {
   visitDuration: number;
   intent: string;
   lastActivity: string;
+  location?: string;
+  device?: string;
+  source?: string;
 }
 
 export interface AutoReplyRule {
@@ -753,8 +801,8 @@ export interface ServicePerformance {
   department: string;
   handleCount: number;
   aiAssistCount: number;
-  avgResponseTime: number;
-  avgFirstResponseTime: number;
+  avgResponseTime: string | number;
+  avgFirstResponseTime: string | number;
   satisfaction: number;
   solveRate: number;
   date: string;
@@ -785,30 +833,61 @@ export interface AIControllerStats {
 
 /* ───── CRM ───── */
 export interface TicketItem {
-  id: string;
+  id: number;
   ticketNo: string;
   title: string;
-  type: string;
+  content: string;
+  ticketType: string;
   priority: string;
   status: string;
-  creator: string;
-  assignee: string;
-  createTime: string;
-  updateTime: string;
+  customerId: number;
+  customerName: string;
+  customerEmail: string;
+  assignedTo: number;
+  assignedName: string;
+  assignedAt: string;
+  resolvedAt: string;
+  resolution: string;
+  createdBy: string;
+  createdAt: string;
+  updatedBy: string;
+  updatedAt: string;
 }
 
 export interface SatisfactionItem {
-  id: string;
+  id: number;
+  satisfactionNo: string;
+  orderId: number;
   orderNo: string;
-  source: string;
-  buyer: string;
-  productTitle: string;
-  rating: number;
-  content: string;
-  reply: string;
-  handleStatus: string;
-  csName: string;
-  createTime: string;
+  customerId: number;
+  customerName: string;
+  score: number;
+  rating: string;
+  feedback: string;
+  response: string;
+  responseBy: string;
+  respondedAt: string;
+  createdAt: string;
+  updatedBy: string;
+  updatedAt: string;
+}
+
+export interface ChatLogItem {
+  id: number;
+  sessionNo: string;
+  sessionId: string;
+  customerId: number;
+  customerName: string;
+  channel: string;
+  messageType: string;
+  messageContent: string;
+  direction: string;
+  senderType: string;
+  senderId: number;
+  senderName: string;
+  agentName: string;
+  status: string;
+  createdAt: string;
 }
 
 /* ───── Marketing ───── */

+ 1 - 1
frontend/src/views/auth/LoginView.vue

@@ -7,7 +7,7 @@
         <p>先登录再进入商品、订单、供应链和履约模块。</p>
 
         <div class="login-accounts">
-          <div v-for="account in demoAccounts" :key="account.username" class="login-account" @click="fillAccount(account.username)">
+          <div v-for="account in demoAccounts" :key="account.username" class="login-account" @click="fillAccount(account)">
             <strong>{{ account.label }}</strong>
             <span>{{ account.username }} / {{ account.password }}</span>
           </div>

+ 5 - 5
frontend/src/views/channel/ChannelConfigView.vue

@@ -143,7 +143,7 @@ const items = ref<ChannelItem[]>([]);
 const loading = ref(false);
 const drawerVisible = ref(false);
 const isEdit = ref(false);
-const editingId = ref('');
+const editingId = ref<number | null>(null);
 const submitting = ref(false);
 const formRef = ref<FormInstance>();
 
@@ -191,7 +191,7 @@ const loadData = async () => {
 const resetForm = () => {
   Object.assign(formData, defaultForm());
   isEdit.value = false;
-  editingId.value = '';
+  editingId.value = null;
 };
 
 const openCreateDrawer = () => {
@@ -202,7 +202,7 @@ const openCreateDrawer = () => {
 const openEditDrawer = (item: ChannelItem) => {
   resetForm();
   isEdit.value = true;
-  editingId.value = item.id;
+  editingId.value = Number(item.id);
   Object.assign(formData, {
     shopName: item.shopName || '',
     appKey: item.appKey || '',
@@ -221,7 +221,7 @@ const handleSubmit = async () => {
   await formRef.value?.validate();
   submitting.value = true;
   try {
-    if (isEdit.value) {
+    if (isEdit.value && editingId.value !== null) {
       await api.updateChannel(editingId.value, { ...formData });
       ElMessage.success('配置已更新');
     } else {
@@ -270,7 +270,7 @@ const toggleSync = async (item: ChannelItem) => {
       `${action}同步`,
       { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }
     );
-    await api.updateChannel(item.id, { syncEnabled: !item.syncEnabled });
+    await api.updateChannel(Number(item.id), { syncEnabled: !item.syncEnabled });
     ElMessage.success(`已${action}同步`);
     loadData();
   } catch {

+ 2 - 2
frontend/src/views/crm/AutoReplyRuleView.vue

@@ -227,8 +227,8 @@ const filteredRules = computed(() => {
   }).sort((a, b) => a.priority - b.priority);
 });
 
-const editForm = ref<Partial<AutoReplyRule>>({});
-const form = ref<Partial<AutoReplyRule>>({ keywords: [], responses: [''], matchMode: 'contain', triggerType: 'keyword' });
+const editForm = ref<Partial<AutoReplyRule> & { responses: string[] }>({ responses: [''] });
+const form = ref<Partial<AutoReplyRule> & { responses: string[] }>({ keywords: [], responses: [''], matchMode: 'contain', triggerType: 'keyword' });
 
 const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
 const resetFilters = () => { filters.value = { name: '', triggerType: '', status: '' }; };

+ 1 - 1
frontend/src/views/crm/ServicePerformanceView.vue

@@ -174,7 +174,7 @@ const rankedAgents = computed(() => {
   return [...performanceData.value].sort((a, b) => {
     if (rankType.value === 'handle') return b.handleCount - a.handleCount;
     if (rankType.value === 'satisfaction') return b.satisfaction - a.satisfaction;
-    return a.avgResponseTime.localeCompare(b.avgResponseTime);
+    return String(a.avgResponseTime).localeCompare(String(b.avgResponseTime));
   });
 });
 

+ 1 - 1
frontend/src/views/finance/PaymentView.vue

@@ -210,7 +210,7 @@ const openReconcile = async () => {
     const idx = items.value.findIndex(p => p.id === item.id);
     if (idx !== -1) items.value[idx].reconcileStatus = '已确认';
     if (item.orderId) {
-      await api.updateOrder(item.orderId, { 
+      await api.updateOrder(Number(item.orderId), { 
         orderStatus: 'paid',
         paymentStatus: 'paid',
         paidAt: new Date().toISOString()

+ 2 - 2
frontend/src/views/finance/RefundView.vue

@@ -186,7 +186,7 @@ const retryRefund = async (row: RefundItem) => {
   const idx = items.value.findIndex(i => i.refundNo === row.refundNo);
   if (idx !== -1) items.value[idx].status = '退款中';
   if (row.orderId) {
-    await api.updateOrder(row.orderId, { 
+    await api.updateOrder(Number(row.orderId), { 
       refundStatus: '部分退款',
       orderStatus: 'refunded'
     } as Partial<OrderItem>);
@@ -201,7 +201,7 @@ const openBatchRefund = async () => {
     const idx = items.value.findIndex(i => i.refundNo === item.refundNo);
     if (idx !== -1) items.value[idx].status = '退款中';
     if (item.orderId) {
-      await api.updateOrder(item.orderId, { 
+      await api.updateOrder(Number(item.orderId), { 
         refundStatus: '部分退款',
         orderStatus: 'refunded'
       } as Partial<OrderItem>);

+ 3 - 3
frontend/src/views/inventory/InventoryOverviewView.vue

@@ -226,7 +226,7 @@ const rowClass = ({ row }: { row: InventoryItem }) => {
 const loadData = async () => {
   loading.value = true;
   try {
-    const res = await api.getInventory();
+    const res = await api.getInventories();
     items.value = res.items;
   } finally {
     loading.value = false;
@@ -244,7 +244,7 @@ const detailItem = ref<InventoryItem | null>(null);
 const openDetailDrawer = async (item: InventoryItem) => {
   detailItem.value = item;
   detailVisible.value = true;
-  const logRes = await api.getInventoryLogs();
+  const logRes = await api.getInventoryLogs(Number(item.id));
   logs.value = logRes.items;
 };
 
@@ -283,7 +283,7 @@ const submitAdjust = async () => {
   }
   adjusting.value = true;
   try {
-    await api.updateInventory(adjustTarget.value.id, {
+    await api.updateInventory(Number(adjustTarget.value.id), {
       available: adjustTarget.value.available + adjustForm.quantity
     } as Partial<InventoryItem>);
     ElMessage.success('库存已调整');

+ 3 - 3
frontend/src/views/inventory/ShippingWorkView.vue

@@ -263,7 +263,7 @@ const submitTracking = async () => {
   }
   submittingTracking.value = true;
   try {
-    await api.updateShippingOrder(trackingTarget.value.id, {
+    await api.updateShippingOrder(Number(trackingTarget.value.id), {
       carrier: trackingForm.carrier,
       trackingNo: trackingForm.trackingNo,
       shippingStatus: '待发货'
@@ -287,7 +287,7 @@ const confirmShipment = async (item: ShippingItem) => {
       '确认发货',
       { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }
     );
-    await api.updateShippingOrder(item.id, {
+    await api.updateShippingOrder(Number(item.id), {
       shippingStatus: '已发货'
     } as Partial<ShippingItem>);
     ElMessage.success('已确认发货');
@@ -304,7 +304,7 @@ const retryReturn = async (item: ShippingItem) => {
       '重试回传',
       { confirmButtonText: '重试', cancelButtonText: '取消', type: 'info' }
     );
-    await api.updateShippingOrder(item.id, {
+    await api.updateShippingOrder(Number(item.id), {
       returnStatus: '已回传'
     } as Partial<ShippingItem>);
     ElMessage.success('回传成功');

+ 14 - 14
frontend/src/views/order/OrderAfterSaleView.vue

@@ -245,7 +245,7 @@ const confirmAudit = async () => {
     return;
   }
   const newStatus = auditAction.value === 'approve' ? '已通过' : '已拒绝';
-  await api.updateAfterSale(auditItem.value!.id, { auditStatus: newStatus } as Partial<AfterSaleItem>);
+  await api.updateAfterSale(Number(auditItem.value!.id), { auditStatus: newStatus } as Partial<AfterSaleItem>);
   auditDialog.value = false;
   ElMessage.success(`已${auditAction.value === 'approve' ? '通过' : '拒绝'}审核`);
   loadData();
@@ -253,9 +253,9 @@ const confirmAudit = async () => {
 
 const doRefund = async (row: AfterSaleItem) => {
   await ElMessageBox.confirm(`确认对 ${row.afterSaleNo} 发起退款 ${row.amount}?`);
-  await api.updateAfterSale(row.id, { refundStatus: '已退款' } as Partial<AfterSaleItem>);
+  await api.updateAfterSale(Number(row.id), { refundStatus: '已退款' } as Partial<AfterSaleItem>);
   if (row.orderId) {
-    await api.updateOrder(row.orderId, { 
+    await api.updateOrder(Number(row.orderId), { 
       refundStatus: '已退款',
       orderStatus: 'refunded'
     } as Partial<OrderItem>);
@@ -273,7 +273,7 @@ const openReturnTracking = (row: AfterSaleItem) => {
 
 const confirmReturnTracking = async () => {
   if (!returnTrackingNo.value) { ElMessage.warning('请输入物流单号'); return; }
-  await api.updateAfterSale(returnTarget.value!.id, { refundStatus: '已退货待入库' } as Partial<AfterSaleItem>);
+  await api.updateAfterSale(Number(returnTarget.value!.id), { refundStatus: '已退货待入库' } as Partial<AfterSaleItem>);
   returnDialog.value = false;
   ElMessage.success('退货物流已录入');
   loadData();
@@ -281,18 +281,18 @@ const confirmReturnTracking = async () => {
 
 const confirmReceipt = async (row: AfterSaleItem) => {
   await ElMessageBox.confirm(`确认已收到 ${row.afterSaleNo} 的退货并入库?`);
-  await api.updateAfterSale(row.id, { refundStatus: '已退款' } as Partial<AfterSaleItem>);
+  await api.updateAfterSale(Number(row.id), { refundStatus: '已退款' } as Partial<AfterSaleItem>);
   
   if (row.orderId) {
-    const orderRes = await api.getOrder(row.orderId);
+    const orderRes = await api.getOrder(Number(row.orderId));
     const order = orderRes;
     for (const item of order.items || []) {
-      const invRes = await api.getInventory();
+      const invRes = await api.getInventories();
       const invItem = invRes.items.find((inv: any) => inv.skuId === item.skuId);
       if (invItem) {
         const newAvailable = (invItem.available || 0) + item.qty;
         const newLocked = Math.max(0, (invItem.locked || 0) - item.qty);
-        await api.updateInventory(invItem.id, {
+        await api.updateInventory(Number(invItem.id), {
           available: newAvailable,
           locked: newLocked
         });
@@ -306,7 +306,7 @@ const confirmReceipt = async (row: AfterSaleItem) => {
         });
       }
     }
-    await api.updateOrder(row.orderId, { 
+    await api.updateOrder(Number(row.orderId), { 
       refundStatus: '已退款',
       orderStatus: 'refunded'
     } as Partial<OrderItem>);
@@ -328,7 +328,7 @@ const confirmResend = async () => {
   if (!resendWarehouse.value || !resendSku.value) { ElMessage.warning('请选择仓库和SKU'); return; }
   
   const originalOrder = resendTarget.value?.orderId 
-    ? await api.getOrder(resendTarget.value.orderId).catch(() => null) 
+    ? await api.getOrder(Number(resendTarget.value.orderId)).catch(() => null) 
     : null;
   
   const newOrderNo = `OMS-RESEND-${Date.now()}`;
@@ -361,17 +361,17 @@ const confirmResend = async () => {
       profit: '0.00',
       profitRate: 0,
       subtotal: '0.00'
-    }]
+    }] as any
   };
   
   await api.createOrder(newOrderData);
   
-  const invRes = await api.getInventory();
+  const invRes = await api.getInventories();
   const invItem = invRes.items.find((inv: any) => inv.sku === resendSku.value);
   if (invItem) {
     const newAvailable = Math.max(0, (invItem.available || 0) - resendQty.value);
     const newLocked = (invItem.locked || 0) + resendQty.value;
-    await api.updateInventory(invItem.id, {
+    await api.updateInventory(Number(invItem.id), {
       available: newAvailable,
       locked: newLocked
     });
@@ -385,7 +385,7 @@ const confirmResend = async () => {
     });
   }
   
-  await api.updateAfterSale(resendTarget.value!.id, { refundStatus: '已补发' } as Partial<AfterSaleItem>);
+  await api.updateAfterSale(Number(resendTarget.value!.id), { refundStatus: '已补发' } as Partial<AfterSaleItem>);
   resendDialog.value = false;
   ElMessage.success(`补发单 ${newOrderNo} 已生成,库存已扣减`);
   loadData();

+ 6 - 5
frontend/src/views/order/OrderDetailView.vue

@@ -546,6 +546,7 @@ const splitDialog = ref(false);
 const mergeDialog = ref(false);
 const assignDrawer = ref(false);
 const editAddressDialog = ref(false);
+const addTrackingDialog = ref(false);
 const cancelReason = ref('');
 const cancelRemark = ref('');
 const mergeTarget = ref('');
@@ -588,7 +589,7 @@ const order = reactive<OrderItem>({
   itemCount: 0, amount: '', items: [], timeline: [], logs: []
 });
 
-const operationLogs = ref<{ time: string; title: string; type: string; operator?: string; operatorRole?: string }[]>([]);
+const operationLogs = ref<{ time: string; title: string; type: string; operator?: string; operatorRole?: string; summary?: string }[]>([]);
 const trackingSteps = ref<{ time: string; title: string; detail?: string; type: string }[]>([]);
 
 const editAddress = reactive({ receiverName: '', receiverPhone: '', receiverCountry: '', receiverState: '', receiverCity: '', receiverPostalCode: '', receiverAddress: '' });
@@ -793,7 +794,7 @@ const lockInventory = () => { ElMessage.success('库存已锁定'); };
 const confirmCancel = async () => {
   if (!cancelReason.value) { ElMessage.warning('请选择取消原因'); return; }
   await ElMessageBox.confirm('确认取消此订单?操作不可撤销。', '二次确认', { type: 'warning' });
-  await api.updateOrder(order.id!, { orderStatus: 'cancelled' } as Partial<OrderItem>);
+  await api.updateOrder(Number(order.id), { orderStatus: 'cancelled' } as Partial<OrderItem>);
   order.orderStatus = 'cancelled';
   operationLogs.value.unshift({ time: '刚刚', title: '订单已取消', summary: cancelReason.value, type: 'danger', operator: '客服', operatorRole: '客服组' });
   cancelDialog.value = false;
@@ -815,7 +816,7 @@ const confirmMerge = () => {
 };
 
 const submitWarehouse = async () => {
-  await api.updateOrder(order.id!, { 
+  await api.updateOrder(Number(order.id), { 
     warehouse: order.warehouse,
     warehouseLocation: order.warehouseLocation,
     orderStatus: 'allocated'
@@ -823,12 +824,12 @@ const submitWarehouse = async () => {
   
   for (const item of order.items || []) {
     if (item.skuId) {
-      const inventoryRes = await api.getInventory();
+      const inventoryRes = await api.getInventories();
       const invItem = inventoryRes.items.find((inv: any) => inv.skuId === item.skuId);
       if (invItem) {
         const newAvailable = Math.max(0, (invItem.available || 0) - item.qty);
         const newLocked = (invItem.locked || 0) + item.qty;
-        await api.updateInventory(invItem.id, {
+        await api.updateInventory(Number(invItem.id), {
           locked: newLocked,
           available: newAvailable
         });

+ 1 - 1
frontend/src/views/order/OrderListView.vue

@@ -432,7 +432,7 @@ const generateMockOrders = (): OrderItem[] => {
     const amount = prods.reduce((s, p) => s + parseFloat(p.subtotal), 0);
     return {
       id: `order-${i}`, orderNo,
-      channelOrderNo: channels[i % 4] === 'Shopify US' ? `CH${Date.now()}${i}` : undefined,
+      channelOrderNo: channels[i % 4] === 'Shopify US' ? `CH${Date.now()}${i}` : '',
       channel: channels[i % channels.length],
       orderStatus: statuses[i % statuses.length],
       shippingStatus: ['unshipped', 'processing', 'shipped'][i % 3],

+ 2 - 2
frontend/src/views/product/ProductEditorView.vue

@@ -312,7 +312,7 @@ const nextStep = async () => {
 
 const saveDraft = async () => {
   if (isEdit.value) {
-    await api.updateProduct(route.query.id as string, { title: form.title, status: '草稿' } as Partial<ProductItem>);
+    await api.updateProduct(Number(route.query.id), { title: form.title, status: '草稿' } as Partial<ProductItem>);
   } else {
     await api.createProduct({ title: form.title, category: form.category, brand: form.brand, status: '草稿' } as Partial<ProductItem>);
   }
@@ -332,7 +332,7 @@ const submitForValidation = () => {
 onMounted(async () => {
   if (isEdit.value) {
     const res = await api.getProducts();
-    const product = res.items.find(p => p.id === route.query.id);
+    const product = res.content.find(p => p.id === route.query.id);
     if (product) {
       form.title = product.title;
       form.category = product.category;

+ 4 - 4
frontend/src/views/product/ProductListView.vue

@@ -190,7 +190,7 @@ const loadData = async () => {
   loading.value = true;
   try {
     const res = await api.getProducts();
-    items.value = res.items;
+    items.value = res.content;
   } finally {
     loading.value = false;
   }
@@ -207,7 +207,7 @@ const onSelection = (rows: ProductItem[]) => {
 const batchAction = async (action: string) => {
   await ElMessageBox.confirm(`确认将选中的 ${selected.value.length} 个商品${action}?`, '批量操作');
   for (const item of selected.value) {
-    await api.updateProduct(item.id, { status: action === '上架' ? '已上架' : '已下架' });
+    await api.updateProduct(Number(item.id), { status: action === '上架' ? '已上架' : '已下架' });
   }
   ElMessage.success(`${action}成功`);
   loadData();
@@ -216,14 +216,14 @@ const batchAction = async (action: string) => {
 const toggleStatus = async (row: ProductItem) => {
   const newStatus = row.status === '已上架' ? '已下架' : '已上架';
   await ElMessageBox.confirm(`确认${newStatus === '已上架' ? '上架' : '下架'}「${row.title}」?`, '操作确认');
-  await api.updateProduct(row.id, { status: newStatus });
+    await api.updateProduct(Number(row.id), { status: newStatus });
   ElMessage.success('操作成功');
   loadData();
 };
 
 const handleDelete = async (row: ProductItem) => {
   await ElMessageBox.confirm(`确认删除「${row.title}」?此操作不可恢复。`, '删除确认', { type: 'warning' });
-  await api.deleteProduct(row.id);
+    await api.deleteProduct(Number(row.id));
   ElMessage.success('已删除');
   loadData();
 };

+ 6 - 6
frontend/src/views/product/ProductMappingView.vue

@@ -172,8 +172,8 @@ const total = computed(() => filteredItems.value.length);
 const loadData = async () => {
   loading.value = true;
   try {
-    const res = await api.getMappings();
-    items.value = res.items;
+    const res = await api.getChannelMappings();
+    items.value = res.content;
   } finally {
     loading.value = false;
   }
@@ -200,10 +200,10 @@ const openDialog = (item?: MappingItem) => {
 
 const saveMapping = async () => {
   if (editingItem.value) {
-    await api.updateMapping(editingItem.value.id, { ...formData, mappingStatus: '已映射', validateStatus: '未校验', lastSyncAt: new Date().toISOString().slice(0, 16).replace('T', ' ') } as Partial<MappingItem>);
+    await api.updateChannelMapping(Number(editingItem.value.id), { ...formData, mappingStatus: '已映射', validateStatus: '未校验', lastSyncAt: new Date().toISOString().slice(0, 16).replace('T', ' ') } as Partial<MappingItem>);
     ElMessage.success('映射已更新');
   } else {
-    await api.createMapping({ ...formData, mappingStatus: '已映射', validateStatus: '未校验', lastSyncAt: '-', lastValidateResult: '-', channelSku: '', productTitle: formData.internalSku } as Partial<MappingItem>);
+    await api.createChannelMapping({ ...formData, mappingStatus: '已映射', validateStatus: '未校验', lastSyncAt: '-', lastValidateResult: '-', channelSku: '', productTitle: formData.internalSku } as Partial<MappingItem>);
     ElMessage.success('映射已创建');
   }
   dialogVisible.value = false;
@@ -218,13 +218,13 @@ const autoMap = () => {
 const validateMapping = async (row: MappingItem) => {
   const passed = Math.random() > 0.3;
   const result = passed ? '校验通过' : '类目属性"材质"未填写';
-  await api.updateMapping(row.id, { validateStatus: passed ? '通过' : '失败', lastValidateResult: result } as Partial<MappingItem>);
+  await api.updateChannelMapping(Number(row.id), { validateStatus: passed ? '通过' : '失败', lastValidateResult: result } as Partial<MappingItem>);
   ElMessage[passed ? 'success' : 'warning'](result);
   loadData();
 };
 
 const publishMapping = async (row: MappingItem) => {
-  await api.updateMapping(row.id, { lastSyncAt: new Date().toISOString().slice(0, 16).replace('T', ' ') } as Partial<MappingItem>);
+  await api.updateChannelMapping(Number(row.id), { lastSyncAt: new Date().toISOString().slice(0, 16).replace('T', ' ') } as Partial<MappingItem>);
   ElMessage.success('发布成功');
   loadData();
 };

+ 3 - 3
frontend/src/views/product/ProductPricingView.vue

@@ -167,7 +167,7 @@ const formData = reactive({
 
 const filteredItems = computed(() => {
   return items.value.filter((item) => {
-    if (filters.value.sku && !item.sku.includes(filters.value.sku)) return false;
+    if (filters.value.sku && !(item.sku || '').includes(filters.value.sku)) return false;
     if (filters.value.currency && item.currency !== filters.value.currency) return false;
     if (filters.value.status && item.status !== filters.value.status) return false;
     return true;
@@ -202,10 +202,10 @@ const openDialog = (item?: PricingRuleItem) => {
 
 const saveRule = async () => {
   if (editingItem.value) {
-    await api.updatePricingRule(editingItem.value.id, { ...formData, discountPrice: formData.discountPrice || '-' } as Partial<PricingRuleItem>);
+    await api.updatePricingRule(Number(editingItem.value.id), { ...formData, discountPrice: formData.discountPrice || '-' } as unknown as Partial<PricingRuleItem>);
     ElMessage.success('规则已更新');
   } else {
-    await api.createPricingRule({ ...formData, productTitle: formData.sku, channelPrice: formData.basePrice, discountPrice: formData.discountPrice || '-', status: '生效中', modifiedBy: '当前用户' } as Partial<PricingRuleItem>);
+    await api.createPricingRule({ ...formData, productTitle: formData.sku, channelPrice: formData.basePrice, discountPrice: formData.discountPrice || '-', status: '生效中', modifiedBy: '当前用户' } as unknown as Partial<PricingRuleItem>);
     ElMessage.success('规则已创建');
   }
   dialogVisible.value = false;

+ 2 - 2
frontend/src/views/report/CustomerAnalysisView.vue

@@ -30,7 +30,7 @@
             </div>
             <div class="stat-box">
               <div class="stat-box__icon" style="background:linear-gradient(135deg,#67C23A,#E6A23C)">
-                <el-icon><UserPlus /></el-icon>
+                <el-icon><Plus /></el-icon>
               </div>
               <div class="stat-box__content">
                 <div class="stat-box__value">4,852</div>
@@ -119,7 +119,7 @@
 
 <script setup lang="ts">
 import { ref, computed, onMounted } from 'vue';
-import { User, UserPlus, Refresh, Wallet } from '@element-plus/icons-vue';
+import { User, Refresh, Wallet, Plus } from '@element-plus/icons-vue';
 import VChart from 'vue-echarts';
 import { use } from 'echarts/core';
 import { CanvasRenderer } from 'echarts/renderers';

+ 1 - 1
frontend/src/views/report/ProcurementReportView.vue

@@ -169,7 +169,7 @@ const purchaseTrendOption = computed(() => ({
     type: 'line',
     smooth: true,
     data: [168000, 185000, 172000, 198000, 182000, 215000, 186000],
-    areaStyle: { opacity: 0.3, color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(64,158,255,0.5)' }, { offset: 1, color: 'rgba(64,158,255,0.05)' } } },
+    areaStyle: { opacity: 0.3, color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(64,158,255,0.5)' }, { offset: 1, color: 'rgba(64,158,255,0.05)' }] } },
     lineStyle: { width: 3, color: '#409EFF' },
     itemStyle: { color: '#409EFF' },
     symbol: 'circle',

+ 4 - 3
frontend/src/views/report/ReportCenterView.vue

@@ -261,7 +261,7 @@ const trendClass = (val: string) => {
 
 const barOption = computed(() => {
   const metrics = reportData.value.map(r => r.metric);
-  const values = reportData.value.map(r => parseFloat(r.value.replace(/[^0-9.]/g, '')) || 0);
+  const values = reportData.value.map(r => parseFloat((r.value || '').replace(/[^0-9.]/g, '')) || 0);
   return {
     tooltip: { trigger: 'axis' as const },
     grid: { left: 80, right: 20, top: 20, bottom: 60 },
@@ -274,8 +274,9 @@ const barOption = computed(() => {
 const lineOption = computed(() => {
   const metrics = reportData.value.map(r => r.metric);
   const momValues = reportData.value.map(r => {
-    const m = r.mom.match(/[\d.]+/);
-    const sign = r.mom.startsWith('-') || r.mom.startsWith('↓') ? -1 : 1;
+    const momStr = r.mom || '';
+    const m = momStr.match(/[\d.]+/);
+    const sign = momStr.startsWith('-') || momStr.startsWith('↓') ? -1 : 1;
     return m ? sign * parseFloat(m[0]) : 0;
   });
   return {

+ 7 - 7
frontend/src/views/supplier/PurchaseOrderView.vue

@@ -384,19 +384,19 @@ const handleArrivalSubmit = async () => {
   submitting.value = true;
   try {
     const po = items.value.find(p => p.id === arrivalForm.poId);
-    await api.updatePurchaseOrder(arrivalForm.poId, {
+    await api.updatePurchaseOrder(Number(arrivalForm.poId), {
       status: 'partial_arrival'
     } as Partial<PurchaseOrderItem>);
     
-    const invRes = await api.getInventory();
+    const invRes = await api.getInventories();
     const invItem = invRes.items.find((inv: any) => inv.sku === arrivalForm.sku);
     if (invItem) {
       const newAvailable = (invItem.available || 0) + arrivalForm.qty;
-      const newInTransit = Math.max(0, (invItem.inTransit || 0) - arrivalForm.qty);
-      await api.updateInventory(invItem.id, {
+      const newInTransit = Math.max(0, ((invItem as any).inTransit || 0) - arrivalForm.qty);
+      await api.updateInventory(Number(invItem.id), {
         available: newAvailable,
         inTransit: newInTransit
-      });
+      } as any);
       await api.createInventoryLog({
         source: '采购到货入库',
         relatedOrder: po?.poNo || arrivalForm.poId,
@@ -424,7 +424,7 @@ const batchConfirmArrival = async () => {
       '批量确认到货'
     );
     for (const po of selected.value) {
-      await api.updatePurchaseOrder(po.id, { status: 'partial_arrival' } as Partial<PurchaseOrderItem>);
+      await api.updatePurchaseOrder(Number(po.id), { status: 'partial_arrival' } as Partial<PurchaseOrderItem>);
     }
     ElMessage.success('批量到货确认成功');
     loadData();
@@ -440,7 +440,7 @@ const closePO = async (row: PurchaseOrderItem) => {
       '关闭确认',
       { type: 'warning' }
     );
-    await api.updatePurchaseOrder(row.id, { status: 'closed' } as Partial<PurchaseOrderItem>);
+    await api.updatePurchaseOrder(Number(row.id), { status: 'closed' } as Partial<PurchaseOrderItem>);
     ElMessage.success('采购单已关闭');
     loadData();
   } catch {

+ 2 - 2
frontend/src/views/supplier/SupplierListView.vue

@@ -276,7 +276,7 @@ const handleSubmit = async () => {
   submitting.value = true;
   try {
     if (isEdit.value) {
-      await api.updateSupplier(editId.value, formData);
+      await api.updateSupplier(Number(editId.value), formData);
       ElMessage.success('供应商更新成功');
     } else {
       await api.createSupplier(formData);
@@ -311,7 +311,7 @@ const toggleStatus = async (row: SupplierItem) => {
     }
   }
 
-  await api.updateSupplier(row.id, { status: targetStatus });
+  await api.updateSupplier(Number(row.id), { status: targetStatus });
   ElMessage.success(`${action}成功`);
   loadData();
 };

+ 5 - 5
frontend/src/views/supplier/SupplyCapabilityView.vue

@@ -256,7 +256,7 @@ const loadData = async () => {
     const [capRes, supplierRes] = await Promise.all([
       api.getSupplyCapabilities(),
       api.getSuppliers()
-    ]);
+    ] as const);
     items.value = capRes.items;
     supplierOptions.value = supplierRes.items.map((s) => s.name);
   } finally {
@@ -327,7 +327,7 @@ const handleSubmit = async () => {
     };
 
     if (isEdit.value) {
-      await api.updateSupplyCapability(editId.value, payload);
+      await api.updateSupplyCapability(Number(editId.value), payload);
       ElMessage.success('供货配置更新成功');
     } else {
       await api.createSupplyCapability(payload);
@@ -353,11 +353,11 @@ const setDefault = async (row: SupplyCapabilityItem) => {
       (item) => item.sku === row.sku && item.isDefault && item.id !== row.id
     );
     for (const item of sameSkuDefaults) {
-      await api.updateSupplyCapability(item.id, { isDefault: false } as Partial<SupplyCapabilityItem>);
+      await api.updateSupplyCapability(Number(item.id), { isDefault: false } as Partial<SupplyCapabilityItem>);
     }
 
     // Set the new default
-    await api.updateSupplyCapability(row.id, { isDefault: true } as Partial<SupplyCapabilityItem>);
+    await api.updateSupplyCapability(Number(row.id), { isDefault: true } as Partial<SupplyCapabilityItem>);
     ElMessage.success('已设为默认供应商');
     loadData();
   } catch {
@@ -375,7 +375,7 @@ const toggleStatus = async (row: SupplyCapabilityItem) => {
       `${action}确认`,
       { type: target === '停用' ? 'warning' : 'info' }
     );
-    await api.updateSupplyCapability(row.id, { status: target } as Partial<SupplyCapabilityItem>);
+    await api.updateSupplyCapability(Number(row.id), { status: target } as Partial<SupplyCapabilityItem>);
     ElMessage.success(`${action}成功`);
     loadData();
   } catch {

+ 3 - 3
frontend/src/views/system/ApiKeyView.vue

@@ -272,7 +272,7 @@ const toggleKeyStatus = async (item: ApiKeyItem) => {
       `${action}确认`,
       { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }
     );
-    await api.updateApiKey(item.id, { status: action } as Partial<ApiKeyItem>);
+    await api.updateApiKey(Number(item.id), { status: action } as Partial<ApiKeyItem>);
     ElMessage.success(`已${action} Key「${item.name}」`);
     loadData();
   } catch {
@@ -290,7 +290,7 @@ const rotateKey = (item: ApiKeyItem) => {
 const submitRotate = async () => {
   rotating.value = true;
   try {
-    await api.updateApiKey(rotatingKeyId.value, {
+    await api.updateApiKey(Number(rotatingKeyId.value), {
       status: '轮换中'
     } as Partial<ApiKeyItem>);
     ElMessage.success(`轮换已启动,旧 Key 将在 ${rotateForm.transitionDays} 天后失效`);
@@ -309,7 +309,7 @@ const deleteKey = async (item: ApiKeyItem) => {
       '删除确认',
       { confirmButtonText: '确认删除', cancelButtonText: '取消', type: 'error' }
     );
-    await api.deleteApiKey(item.id);
+    await api.deleteApiKey(Number(item.id));
     ElMessage.success(`已删除 Key「${item.name}」`);
     loadData();
   } catch {

+ 1 - 1
frontend/src/views/system/DepartmentView.vue

@@ -186,7 +186,7 @@ const buildTree = (depts: DeptItem[]): DeptItem[] => {
 const loadData = () => {
   loading.value = true;
   treeData.value = buildTree(rawDepts.value);
-  selectTreeData.value = [{ id: '', name: '无上级(顶级部门)' }, ...rawDepts.value.filter(d => !d.parentId)];
+  selectTreeData.value = [{ id: '', name: '无上级(顶级部门)', description: '', status: '' } as DeptItem, ...rawDepts.value.filter(d => !d.parentId)];
   setTimeout(() => { loading.value = false; }, 300);
 };
 

+ 3 - 1
frontend/src/views/system/MessageTemplateView.vue

@@ -91,7 +91,7 @@
         </el-form-item>
         <el-form-item label="可用变量">
           <div style="color:var(--cb-text-soft);font-size:13px">
-            {{orderNo}} 订单号 | {{buyerName}} 买家姓名 | {{orderAmount}} 订单金额 | {{trackingNo}} 运单号 | {{sku}} SKU | {{productName}} 商品名称
+            {{ variablePlaceholders }}
           </div>
         </el-form-item>
         <el-form-item label="备注">
@@ -145,6 +145,8 @@ const formData = reactive({
   remark: ''
 });
 
+const variablePlaceholders = '{{orderNo}} 订单号 | {{buyerName}} 买家姓名 | {{orderAmount}} 订单金额 | {{trackingNo}} 运单号 | {{sku}} SKU | {{productName}} 商品名称';
+
 const filteredItems = computed(() => {
   return items.value.filter(item => {
     if (filters.value.name && !item.name.includes(filters.value.name)) return false;

+ 2 - 2
frontend/src/views/system/RolePermissionView.vue

@@ -360,7 +360,7 @@ const toggleRoleStatus = async (item: RoleItem) => {
     }
   }
 
-  await api.updateRole(item.id, { status: action } as Partial<RoleItem>);
+  await api.updateRole(Number(item.id), { status: action } as Partial<RoleItem>);
   ElMessage.success(`已${action}角色「${item.name}」`);
   loadData();
 };
@@ -395,7 +395,7 @@ const submitRole = async () => {
     };
 
     if (isEditRole.value) {
-      await api.updateRole(editingRoleId.value, payload as Partial<RoleItem>);
+      await api.updateRole(Number(editingRoleId.value), payload as Partial<RoleItem>);
       ElMessage.success('角色权限已更新');
     } else {
       await api.createRole(payload as Partial<RoleItem>);

+ 5 - 17
frontend/src/views/warehouse/WarehouseView.vue

@@ -138,24 +138,11 @@ import { computed, onMounted, reactive, ref } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import type { WarehouseItem } from '@/types/page';
 
-interface WarehouseItem {
-  id: string;
-  name: string;
-  type: string;
-  address: string;
-  contact: string;
-  phone: string;
-  area: number;
-  manager: string;
-  status: string;
-  updatedAt: string;
-}
-
 const items = ref<WarehouseItem[]>([
-  { id: 'W001', name: '深圳南山仓', type: '自有仓', address: '广东省深圳市南山区科技园南路88号', contact: '张明', phone: '138-0013-8000', area: 1200, manager: '张明', status: '启用', updatedAt: '2026-04-15 10:30' },
-  { id: 'W002', name: '义乌商贸仓', type: '第三方仓', address: '浙江省义乌市稠城街道国际商贸城', contact: '李华', phone: '139-5739-5739', area: 2500, manager: '李华', status: '启用', updatedAt: '2026-04-18 14:20' },
-  { id: 'W003', name: '洛杉矶海外仓', type: '第三方仓', address: '1500 S Alamed St, Los Angeles, CA 90015', contact: 'Jack', phone: '+1-213-555-8800', area: 3500, manager: 'Jack', status: '启用', updatedAt: '2026-04-10 09:00' },
-  { id: 'W004', name: '伦敦海外仓', type: '第三方仓', address: '45 Industrial Estate, London E16 2LY', contact: 'Tom', phone: '+44-20-7946-0000', area: 2000, manager: 'Tom', status: '停用', updatedAt: '2026-03-28 16:45' }
+  { id: 'W001', name: '深圳南山仓', type: '自有仓', address: '广东省深圳市南山区科技园南路88号', contact: '张明', phone: '138-0013-8000', area: 1200, manager: '张明', status: '启用', remark: '', updatedAt: '2026-04-15 10:30' },
+  { id: 'W002', name: '义乌商贸仓', type: '第三方仓', address: '浙江省义乌市稠城街道国际商贸城', contact: '李华', phone: '139-5739-5739', area: 2500, manager: '李华', status: '启用', remark: '', updatedAt: '2026-04-18 14:20' },
+  { id: 'W003', name: '洛杉矶海外仓', type: '第三方仓', address: '1500 S Alamed St, Los Angeles, CA 90015', contact: 'Jack', phone: '+1-213-555-8800', area: 3500, manager: 'Jack', status: '启用', remark: '', updatedAt: '2026-04-10 09:00' },
+  { id: 'W004', name: '伦敦海外仓', type: '第三方仓', address: '45 Industrial Estate, London E16 2LY', contact: 'Tom', phone: '+44-20-7946-0000', area: 2000, manager: 'Tom', status: '停用', remark: '', updatedAt: '2026-03-28 16:45' }
 ]);
 
 const loading = ref(false);
@@ -259,6 +246,7 @@ const saveWarehouse = () => {
       area: formData.area,
       manager: formData.manager,
       status: '启用',
+      remark: '',
       updatedAt: new Date().toLocaleString()
     });
     ElMessage.success('仓库已创建');