Procházet zdrojové kódy

feat: 修复OMS核心业务闭环,添加AI客服模块

核心闭环修复:
- 支付对账后自动更新订单状态为已支付
- 退款/批量退款后同步更新订单退款状态
- 订单分配仓库时锁定库存并记录流水日志
- 采购到货确认后更新库存可用数并记录流水
- 退货入库确认后回退库存并更新订单状态
- 补发单生成真实订单并扣减锁定库存

新增:
- AI客服模块6个页面(工作台/知识库/自动回复/对话日志/绩效/渠道配置)
- api.createOrder() 创建订单方法
- api.createInventoryLog() 库存流水记录方法
- AfterSaleItem.orderId 字段支持关联追溯

优化:
- 菜单结构优化,解决渠道配置路由冲突
- 订单详情页UI优化,操作按钮下拉分组
- 菜单图标和层级结构完善
docker před 2 měsíci
rodič
revize
b763392c3d

+ 67 - 2
src/api/services.ts

@@ -3,6 +3,10 @@ import type {
   AfterSaleItem,
   ApiKeyItem,
   ApprovalFlowItem,
+  AutoReplyRule,
+  ChatChannel,
+  ChatMessage,
+  ChatSession,
   ChannelItem,
   CouponItem,
   DashboardAlert,
@@ -14,11 +18,14 @@ import type {
   InventoryTurnoverItem,
   InvoiceItem,
   IQCItem,
+  KnowledgeBaseItem,
+  KnowledgeCategory,
   LogItem,
   LogisticsProviderItem,
   MappingItem,
   MessageTemplateItem,
   NotificationItem,
+  OnlineVisitor,
   OrderItem,
   PaymentItem,
   PriceWatchItem,
@@ -34,6 +41,7 @@ import type {
   ReturnPackageItem,
   RoleItem,
   SatisfactionItem,
+  ServicePerformance,
   ShippingItem,
   ShippingTemplateItem,
   SupplierItem,
@@ -42,7 +50,8 @@ import type {
   SupplierSettlementItem,
   TicketItem,
   UserProfile,
-  WarehouseItem
+  WarehouseItem,
+  AIControllerStats
 } from '@/types/page';
 
 export interface DashboardOverviewResponse {
@@ -96,6 +105,8 @@ export const api = {
   /* Orders */
   getOrders: () => request<{ items: OrderItem[] }>('/api/orders'),
   getOrder: (id: string) => request<OrderItem>(`/api/orders/${id}`),
+  createOrder: (data: Partial<OrderItem>) =>
+    request<OrderItem>('/api/orders', { method: 'POST', body: JSON.stringify(data) }),
   updateOrder: (id: string, data: Partial<OrderItem>) =>
     request<OrderItem>(`/api/orders/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
 
@@ -135,6 +146,8 @@ export const api = {
   /* Inventory */
   getInventory: () => request<{ items: InventoryItem[] }>('/api/inventory'),
   getInventoryLogs: () => request<{ items: InventoryLogItem[] }>('/api/inventory-logs'),
+  createInventoryLog: (data: Partial<InventoryLogItem>) =>
+    request<InventoryLogItem>('/api/inventory-logs', { method: 'POST', body: JSON.stringify(data) }),
   updateInventory: (id: string, data: Partial<InventoryItem>) =>
     request<InventoryItem>(`/api/inventory/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
 
@@ -309,5 +322,57 @@ export const api = {
   /* Supplier: Performance */
   getSupplierPerformances: () => request<{ items: SupplierPerformanceItem[] }>('/api/supplier/performances'),
   createSupplierPerformance: (data: Partial<SupplierPerformanceItem>) =>
-    request<SupplierPerformanceItem>('/api/supplier/performances', { method: 'POST', body: JSON.stringify(data) })
+    request<SupplierPerformanceItem>('/api/supplier/performances', { method: 'POST', body: JSON.stringify(data) }),
+
+  /* CRM: AI Controller Stats */
+  getAIControllerStats: () => request<AIControllerStats>('/api/crm/ai-controller/stats'),
+  getOnlineVisitors: () => request<{ items: OnlineVisitor[] }>('/api/crm/ai-controller/visitors'),
+  getChatSessions: () => request<{ items: ChatSession[] }>('/api/crm/ai-controller/sessions'),
+  getChatSession: (id: string) => request<ChatSession>(`/api/crm/ai-controller/sessions/${id}`),
+  sendChatMessage: (sessionId: string, message: Partial<ChatMessage>) =>
+    request<ChatMessage>(`/api/crm/ai-controller/sessions/${sessionId}/messages`, { method: 'POST', body: JSON.stringify(message) }),
+  transferToAgent: (sessionId: string, agentId: string) =>
+    request<{ success: boolean }>(`/api/crm/ai-controller/sessions/${sessionId}/transfer`, { method: 'POST', body: JSON.stringify({ agentId }) }),
+
+  /* CRM: Knowledge Base */
+  getKnowledgeCategories: () => request<{ items: KnowledgeCategory[] }>('/api/crm/knowledge/categories'),
+  createKnowledgeCategory: (data: Partial<KnowledgeCategory>) =>
+    request<KnowledgeCategory>('/api/crm/knowledge/categories', { method: 'POST', body: JSON.stringify(data) }),
+  updateKnowledgeCategory: (id: string, data: Partial<KnowledgeCategory>) =>
+    request<KnowledgeCategory>(`/api/crm/knowledge/categories/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteKnowledgeCategory: (id: string) => request<{ success: boolean }>(`/api/crm/knowledge/categories/${id}`, { method: 'DELETE' }),
+  getKnowledgeItems: () => request<{ items: KnowledgeBaseItem[] }>('/api/crm/knowledge/items'),
+  createKnowledgeItem: (data: Partial<KnowledgeBaseItem>) =>
+    request<KnowledgeBaseItem>('/api/crm/knowledge/items', { method: 'POST', body: JSON.stringify(data) }),
+  updateKnowledgeItem: (id: string, data: Partial<KnowledgeBaseItem>) =>
+    request<KnowledgeBaseItem>(`/api/crm/knowledge/items/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteKnowledgeItem: (id: string) => request<{ success: boolean }>(`/api/crm/knowledge/items/${id}`, { method: 'DELETE' }),
+
+  /* CRM: Auto Reply Rules */
+  getAutoReplyRules: () => request<{ items: AutoReplyRule[] }>('/api/crm/auto-reply/rules'),
+  createAutoReplyRule: (data: Partial<AutoReplyRule>) =>
+    request<AutoReplyRule>('/api/crm/auto-reply/rules', { method: 'POST', body: JSON.stringify(data) }),
+  updateAutoReplyRule: (id: string, data: Partial<AutoReplyRule>) =>
+    request<AutoReplyRule>(`/api/crm/auto-reply/rules/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteAutoReplyRule: (id: string) => request<{ success: boolean }>(`/api/crm/auto-reply/rules/${id}`, { method: 'DELETE' }),
+  testAutoReplyRule: (id: string, testMessage: string) =>
+    request<{ response: string }>(`/api/crm/auto-reply/rules/${id}/test`, { method: 'POST', body: JSON.stringify({ message: testMessage }) }),
+
+  /* CRM: Chat Log */
+  getChatLogs: () => request<{ items: ChatSession[] }>('/api/crm/chat-logs'),
+  getChatLogDetail: (id: string) => request<ChatSession>(`/api/crm/chat-logs/${id}`),
+  markChatLog: (id: string, tag: string) =>
+    request<{ success: boolean }>(`/api/crm/chat-logs/${id}/mark`, { method: 'POST', body: JSON.stringify({ tag }) }),
+
+  /* CRM: Service Performance */
+  getServicePerformance: () => request<{ items: ServicePerformance[] }>('/api/crm/service/performance'),
+  getServicePerformanceSummary: () => request<{ stats: DashboardStat[] }>('/api/crm/service/performance/summary'),
+
+  /* CRM: Chat Channels */
+  getChatChannels: () => request<{ items: ChatChannel[] }>('/api/crm/channels'),
+  createChatChannel: (data: Partial<ChatChannel>) =>
+    request<ChatChannel>('/api/crm/channels', { method: 'POST', body: JSON.stringify(data) }),
+  updateChatChannel: (id: string, data: Partial<ChatChannel>) =>
+    request<ChatChannel>(`/api/crm/channels/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
+  deleteChatChannel: (id: string) => request<{ success: boolean }>(`/api/crm/channels/${id}`, { method: 'DELETE' })
 };

+ 99 - 14
src/layout/AppLayout.vue

@@ -1,6 +1,6 @@
 <template>
   <el-container class="app-layout">
-    <el-aside :width="appStore.menuCollapsed ? '84px' : '280px'" class="app-layout__aside">
+    <el-aside :width="appStore.menuCollapsed ? '84px' : '260px'" class="app-layout__aside">
       <div class="brand glass-card">
         <div class="brand__mark">CB</div>
         <div v-if="!appStore.menuCollapsed" class="brand__meta">
@@ -10,13 +10,29 @@
       </div>
 
       <div class="menu-groups glass-card">
-        <el-menu :default-active="route.path" :collapse="appStore.menuCollapsed" router class="menu-groups__menu">
+        <el-menu 
+          :default-active="route.path" 
+          :collapse="appStore.menuCollapsed" 
+          :default-openeds="defaultOpeneds"
+          :collapse-transition="true"
+          router 
+          class="menu-groups__menu"
+        >
           <template v-for="group in filteredGroups" :key="group.key">
-            <el-menu-item-group :title="appStore.menuCollapsed ? '' : group.title">
-              <el-menu-item v-for="item in group.items" :key="item.key" :index="item.path">
+            <el-sub-menu :index="group.key">
+              <template #title>
+                <component :is="getIcon(group.icon)" class="menu-icon" />
+                <span>{{ group.title }}</span>
+              </template>
+              <el-menu-item 
+                v-for="item in group.items" 
+                :key="item.key" 
+                :index="item.path"
+              >
+                <component :is="getIcon(item.icon)" class="menu-icon" />
                 <span>{{ item.title }}</span>
               </el-menu-item>
-            </el-menu-item-group>
+            </el-sub-menu>
           </template>
         </el-menu>
       </div>
@@ -63,8 +79,9 @@
 </template>
 
 <script setup lang="ts">
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import { RouterView, useRoute, useRouter } from 'vue-router';
+import * as ElementPlusIconsVue from '@element-plus/icons-vue';
 import { menuGroups } from '@/router/menu';
 import { useAppStore } from '@/stores/app';
 import { useAuthStore } from '@/stores/auth';
@@ -74,6 +91,8 @@ const authStore = useAuthStore();
 const route = useRoute();
 const router = useRouter();
 
+const defaultOpeneds = ref<string[]>(['datacenter']);
+
 const currentTitle = computed(() => (route.meta.title as string) || 'CrossBorder OS');
 const filteredGroups = computed(() =>
   menuGroups
@@ -84,6 +103,12 @@ const filteredGroups = computed(() =>
     .filter((group) => group.items.length > 0)
 );
 
+const getIcon = (iconName?: string) => {
+  if (!iconName) return ElementPlusIconsVue.View;
+  const icon = ElementPlusIconsVue[iconName as keyof typeof ElementPlusIconsVue];
+  return icon || ElementPlusIconsVue.View;
+};
+
 const goLogin = () => {
   authStore.logout();
   router.push('/login');
@@ -102,6 +127,7 @@ const logout = () => {
 
 .app-layout__aside {
   padding: 18px 12px 18px 18px;
+  transition: width 0.3s ease;
 }
 
 .brand {
@@ -121,12 +147,14 @@ const logout = () => {
   background: linear-gradient(135deg, #0f766e, #d97706);
   color: white;
   font-weight: 800;
+  flex-shrink: 0;
 }
 
 .brand__meta {
   display: flex;
   flex-direction: column;
   gap: 4px;
+  overflow: hidden;
 }
 
 .brand__meta span {
@@ -145,18 +173,75 @@ const logout = () => {
   background: transparent;
 }
 
-:deep(.el-menu-item-group__title) {
-  font-weight: 700;
-  color: var(--cb-text-soft);
+.menu-icon {
+  width: 18px;
+  height: 18px;
+  margin-right: 10px;
+  vertical-align: middle;
+  flex-shrink: 0;
 }
 
-:deep(.el-menu-item) {
-  margin: 6px 0;
-  border-radius: 14px;
+:deep(.el-sub-menu__title) {
+  height: 48px;
+  line-height: 48px;
+  padding: 0 12px !important;
+  margin: 4px 0;
+  border-radius: 12px;
+  font-weight: 600;
+  color: var(--cb-text);
+  transition: all 0.2s ease;
 }
 
-:deep(.el-menu-item.is-active) {
+:deep(.el-sub-menu__title:hover) {
+  background: rgba(15, 118, 110, 0.08);
+}
+
+:deep(.el-sub-menu .el-menu-item) {
+  height: 44px;
+  line-height: 44px;
+  padding: 0 12px 0 48px !important;
+  margin: 2px 0;
+  border-radius: 10px;
+  color: var(--cb-text);
+  transition: all 0.2s ease;
+}
+
+:deep(.el-sub-menu .el-menu-item:hover) {
+  background: rgba(15, 118, 110, 0.08);
+}
+
+:deep(.el-sub-menu .el-menu-item.is-active) {
   background: rgba(15, 118, 110, 0.14);
+  color: var(--cb-primary);
+  font-weight: 600;
+}
+
+:deep(.el-menu--collapse .el-sub-menu__title) {
+  padding: 0 !important;
+  justify-content: center;
+}
+
+:deep(.el-menu--collapse .el-sub-menu .el-menu-item) {
+  padding: 0 !important;
+  justify-content: center;
+}
+
+:deep(.el-menu--collapse .menu-icon) {
+  margin-right: 0;
+}
+
+:deep(.el-sub-menu__icon-arrow) {
+  font-size: 12px;
+  margin-left: auto;
+  right: 12px;
+}
+
+:deep(.el-menu--collapse .el-sub-menu__icon-arrow) {
+  display: none;
+}
+
+:deep(.el-sub-menu.is-active > .el-sub-menu__title) {
+  color: var(--cb-primary);
 }
 
 .app-layout__header {
@@ -231,4 +316,4 @@ const logout = () => {
     align-items: flex-start;
   }
 }
-</style>
+</style>

+ 80 - 49
src/router/menu.ts

@@ -5,11 +5,13 @@ export interface MenuItem {
   path: string;
   roles: UserRole[];
   title: string;
+  icon?: string;
 }
 
 export interface MenuGroup {
   key: string;
   title: string;
+  icon?: string;
   items: MenuItem[];
 }
 
@@ -17,131 +19,160 @@ export const menuGroups: MenuGroup[] = [
   {
     key: 'dashboard',
     title: '运营看板',
+    icon: 'DataBoard',
     items: [
-      { key: 'report-dashboard', title: '运营看板', path: '/report/dashboard', roles: ['admin', 'manager', 'operator', 'procurement'] }
+      { key: 'report-dashboard', title: '运营看板', path: '/report/dashboard', roles: ['admin', 'manager', 'operator', 'procurement'], icon: 'DataBoard' }
     ]
   },
   {
     key: 'datacenter',
     title: '数据中心',
+    icon: 'DataAnalysis',
     items: [
-      { key: 'report-center', title: '报表中心', path: '/report/center', roles: ['admin', 'manager', 'operator', 'procurement'] },
-      { key: 'sales-analysis', title: '销售分析', path: '/report/sales-analysis', roles: ['admin', 'manager', 'operator'] },
-      { key: 'profit-analysis', title: '利润分析', path: '/report/profit-analysis', roles: ['admin', 'manager', 'finance'] },
-      { key: 'inventory-turnover', title: '库存周转分析', path: '/report/inventory-turnover', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'inventory-age', title: '库存库龄分析', path: '/report/inventory-age', roles: ['admin', 'manager', 'procurement', 'warehouse'] },
-      { key: 'customer-analysis', title: '客户分析', path: '/report/customer-analysis', roles: ['admin', 'manager', 'operator'] },
-      { key: 'logistics-report', title: '物流报表', path: '/report/logistics-report', roles: ['admin', 'manager', 'warehouse'] },
-      { key: 'channel-performance', title: '渠道绩效', path: '/report/channel-performance', roles: ['admin', 'manager', 'operator'] },
-      { key: 'procurement-report', title: '采购报表', path: '/report/procurement-report', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'marketing-report', title: '营销报表', path: '/report/marketing-report', roles: ['admin', 'manager', 'operator'] }
+      { key: 'report-center', title: '报表中心', path: '/report/center', roles: ['admin', 'manager', 'operator', 'procurement'], icon: 'PieChart' },
+      { key: 'sales-analysis', title: '销售分析', path: '/report/sales-analysis', roles: ['admin', 'manager', 'operator'], icon: 'TrendCharts' },
+      { key: 'profit-analysis', title: '利润分析', path: '/report/profit-analysis', roles: ['admin', 'manager', 'finance'], icon: 'Money' },
+      { key: 'inventory-turnover', title: '库存周转分析', path: '/report/inventory-turnover', roles: ['admin', 'manager', 'procurement'], icon: 'Odometer' },
+      { key: 'inventory-age', title: '库存库龄分析', path: '/report/inventory-age', roles: ['admin', 'manager', 'procurement', 'warehouse'], icon: 'Clock' },
+      { key: 'customer-analysis', title: '客户分析', path: '/report/customer-analysis', roles: ['admin', 'manager', 'operator'], icon: 'User' },
+      { key: 'logistics-report', title: '物流报表', path: '/report/logistics-report', roles: ['admin', 'manager', 'warehouse'], icon: 'Van' },
+      { key: 'channel-performance', title: '渠道绩效', path: '/report/channel-performance', roles: ['admin', 'manager', 'operator'], icon: 'Trophy' },
+      { key: 'procurement-report', title: '采购报表', path: '/report/procurement-report', roles: ['admin', 'manager', 'procurement'], icon: 'Notebook' },
+      { key: 'marketing-report', title: '营销报表', path: '/report/marketing-report', roles: ['admin', 'manager', 'operator'], icon: 'Promotion' }
     ]
   },
   {
     key: 'product',
     title: '商品中心',
+    icon: 'Goods',
     items: [
-      { key: 'product-list', title: '商品列表', path: '/product/list', roles: ['admin', 'manager', 'operator'] },
-      { key: 'product-mapping', title: '渠道映射', path: '/product/mapping', roles: ['admin', 'manager', 'operator'] },
-      { key: 'product-pricing', title: '定价与库存规则', path: '/product/pricing', roles: ['admin', 'manager', 'operator'] }
+      { key: 'product-list', title: '商品列表', path: '/product/list', roles: ['admin', 'manager', 'operator'], icon: 'List' },
+      { key: 'product-mapping', title: '渠道映射', path: '/product/mapping', roles: ['admin', 'manager', 'operator'], icon: 'Connection' },
+      { key: 'product-pricing', title: '定价与库存规则', path: '/product/pricing', roles: ['admin', 'manager', 'operator'], icon: 'PriceTag' }
     ]
   },
   {
     key: 'orders',
     title: '订单中心',
+    icon: 'Document',
     items: [
-      { key: 'order-list', title: '订单列表', path: '/order/list', roles: ['admin', 'manager', 'operator', 'customer_service', 'warehouse'] },
-      { key: 'order-after-sale', title: '售后处理', path: '/order/after-sale', roles: ['admin', 'manager', 'operator', 'customer_service'] }
+      { key: 'order-list', title: '订单列表', path: '/order/list', roles: ['admin', 'manager', 'operator', 'customer_service', 'warehouse'], icon: 'List' },
+      { key: 'order-after-sale', title: '售后处理', path: '/order/after-sale', roles: ['admin', 'manager', 'operator', 'customer_service'], icon: 'Service' }
     ]
   },
   {
     key: 'supplier',
     title: '供应商中心',
+    icon: 'Box',
     items: [
-      { key: 'supplier-list', title: '供应商管理', path: '/supplier/list', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'purchase-orders', title: '采购单管理', path: '/supplier/purchase-orders', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'supply-capability', title: '供货能力配置', path: '/supplier/capability', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'supplier-performance', title: '供应商绩效', path: '/supplier/performance', roles: ['admin', 'manager', 'procurement'] }
+      { key: 'supplier-list', title: '供应商管理', path: '/supplier/list', roles: ['admin', 'manager', 'procurement'], icon: 'Box' },
+      { key: 'purchase-orders', title: '采购单管理', path: '/supplier/purchase-orders', roles: ['admin', 'manager', 'procurement'], icon: 'Document' },
+      { key: 'supply-capability', title: '供货能力配置', path: '/supplier/capability', roles: ['admin', 'manager', 'procurement'], icon: 'SetUp' },
+      { key: 'supplier-performance', title: '供应商绩效', path: '/supplier/performance', roles: ['admin', 'manager', 'procurement'], icon: 'TrendCharts' }
     ]
   },
   {
     key: 'channel',
     title: '渠道中心',
-    items: [{ key: 'channel-config', title: '渠道接入配置', path: '/channel/config', roles: ['admin', 'manager', 'operator'] }]
+    icon: 'Connection',
+    items: [
+      { key: 'channel-config', title: '渠道接入配置', path: '/channel/config', roles: ['admin', 'manager', 'operator'], icon: 'Connection' }
+    ]
   },
   {
     key: 'inventory',
     title: '库存与履约',
+    icon: 'House',
     items: [
-      { key: 'inventory-overview', title: '库存总览', path: '/inventory/overview', roles: ['admin', 'manager', 'operator', 'procurement', 'warehouse'] },
-      { key: 'inventory-logs', title: '库存变动记录', path: '/inventory/logs', roles: ['admin', 'manager', 'operator', 'warehouse'] },
-      { key: 'warehouse-manage', title: '仓库管理', path: '/warehouse/manage', roles: ['admin', 'manager'] },
-      { key: 'shipping-work', title: '发货作业', path: '/inventory/shipping', roles: ['admin', 'manager', 'warehouse'] }
+      { key: 'inventory-overview', title: '库存总览', path: '/inventory/overview', roles: ['admin', 'manager', 'operator', 'procurement', 'warehouse'], icon: 'DataBoard' },
+      { key: 'inventory-logs', title: '库存变动记录', path: '/inventory/logs', roles: ['admin', 'manager', 'operator', 'warehouse'], icon: 'Document' },
+      { key: 'warehouse-manage', title: '仓库管理', path: '/warehouse/manage', roles: ['admin', 'manager'], icon: 'House' },
+      { key: 'shipping-work', title: '发货作业', path: '/inventory/shipping', roles: ['admin', 'manager', 'warehouse'], icon: 'Van' }
     ]
   },
   {
     key: 'logistics',
     title: '物流中心',
+    icon: 'Van',
     items: [
-      { key: 'logistics-providers', title: '物流商配置', path: '/logistics/providers', roles: ['admin', 'manager', 'operator'] },
-      { key: 'shipping-templates', title: '运费模板', path: '/logistics/templates', roles: ['admin', 'manager', 'operator'] }
+      { key: 'logistics-providers', title: '物流商配置', path: '/logistics/providers', roles: ['admin', 'manager', 'operator'], icon: 'Van' },
+      { key: 'shipping-templates', title: '运费模板', path: '/logistics/templates', roles: ['admin', 'manager', 'operator'], icon: 'Document' }
     ]
   },
   {
     key: 'finance',
     title: '财务中心',
+    icon: 'Money',
     items: [
-      { key: 'finance-payments', title: '收款管理', path: '/finance/payments', roles: ['admin', 'manager', 'finance'] },
-      { key: 'finance-refund', title: '退款管理', path: '/finance/refund', roles: ['admin', 'manager', 'finance'] },
-      { key: 'supplier-settlement', title: '供应商结算', path: '/finance/settlement', roles: ['admin', 'manager', 'finance'] },
-      { key: 'finance-invoice', title: '发票管理', path: '/finance/invoice', roles: ['admin', 'manager', 'finance'] }
+      { key: 'finance-payments', title: '收款管理', path: '/finance/payments', roles: ['admin', 'manager', 'finance'], icon: 'Money' },
+      { key: 'finance-refund', title: '退款管理', path: '/finance/refund', roles: ['admin', 'manager', 'finance'], icon: 'RefreshLeft' },
+      { key: 'supplier-settlement', title: '供应商结算', path: '/finance/settlement', roles: ['admin', 'manager', 'finance'], icon: 'Notebook' },
+      { key: 'finance-invoice', title: '发票管理', path: '/finance/invoice', roles: ['admin', 'manager', 'finance'], icon: 'Document' }
     ]
   },
   {
     key: 'procurement',
     title: '采购中心',
+    icon: 'ShoppingCart',
     items: [
-      { key: 'replenishment-plan', title: '备货计划', path: '/procurement/replenishment', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'purchase-request', title: '采购需求申请', path: '/procurement/purchase-request', roles: ['admin', 'manager', 'procurement'] },
-      { key: 'procurement-iqc', title: '来料质检', path: '/procurement/iqc', roles: ['admin', 'manager', 'procurement', 'warehouse'] }
+      { key: 'replenishment-plan', title: '备货计划', path: '/procurement/replenishment', roles: ['admin', 'manager', 'procurement'], icon: 'Notebook' },
+      { key: 'purchase-request', title: '采购需求申请', path: '/procurement/purchase-request', roles: ['admin', 'manager', 'procurement'], icon: 'Document' },
+      { key: 'procurement-iqc', title: '来料质检', path: '/procurement/iqc', roles: ['admin', 'manager', 'procurement', 'warehouse'], icon: 'Check' }
     ]
   },
   {
     key: 'crm',
     title: '客服中心',
+    icon: 'ChatLineRound',
+    items: [
+      { key: 'crm-tickets', title: '工单管理', path: '/crm/tickets', roles: ['admin', 'manager', 'customer_service'], icon: 'Tickets' },
+      { key: 'crm-satisfaction', title: '满意度评价', path: '/crm/satisfaction', roles: ['admin', 'manager', 'customer_service'], icon: 'Star' }
+    ]
+  },
+  {
+    key: 'ai-service',
+    title: 'AI智能客服',
+    icon: 'Robot',
     items: [
-      { key: 'crm-tickets', title: '工单管理', path: '/crm/tickets', roles: ['admin', 'manager', 'customer_service'] },
-      { key: 'crm-satisfaction', title: '满意度评价', path: '/crm/satisfaction', roles: ['admin', 'manager', 'customer_service'] }
+      { key: 'ai-controller', title: 'AI客服工作台', path: '/crm/ai-controller', roles: ['admin', 'manager', 'customer_service'], icon: 'Service' },
+      { key: 'knowledge-base', title: '知识库管理', path: '/crm/knowledge-base', roles: ['admin', 'manager', 'customer_service'], icon: 'Collection' },
+      { key: 'auto-reply', title: '自动回复规则', path: '/crm/auto-reply', roles: ['admin', 'manager', 'customer_service'], icon: 'ChatDotRound' },
+      { key: 'chat-log', title: '对话日志', path: '/crm/chat-log', roles: ['admin', 'manager', 'customer_service'], icon: 'Document' },
+      { key: 'service-performance', title: '客服绩效', path: '/crm/service-performance', roles: ['admin', 'manager'], icon: 'TrendCharts' },
+      { key: 'ai-channel-config', title: '渠道配置', path: '/crm/ai-channel-config', roles: ['admin', 'manager'], icon: 'Connection' }
     ]
   },
   {
     key: 'marketing',
     title: '营销中心',
+    icon: 'Trophy',
     items: [
-      { key: 'marketing-promotion', title: '促销活动', path: '/marketing/promotion', roles: ['admin', 'manager', 'operator'] },
-      { key: 'marketing-coupon', title: '优惠券管理', path: '/marketing/coupon', roles: ['admin', 'manager', 'operator'] },
-      { key: 'marketing-price-watch', title: '价格预警', path: '/marketing/price-watch', roles: ['admin', 'manager', 'operator'] }
+      { key: 'marketing-promotion', title: '促销活动', path: '/marketing/promotion', roles: ['admin', 'manager', 'operator'], icon: 'Promotion' },
+      { key: 'marketing-coupon', title: '优惠券管理', path: '/marketing/coupon', roles: ['admin', 'manager', 'operator'], icon: 'Discount' },
+      { key: 'marketing-price-watch', title: '价格预警', path: '/marketing/price-watch', roles: ['admin', 'manager', 'operator'], icon: 'Bell' }
     ]
   },
   {
     key: 'system',
     title: '系统与权限',
+    icon: 'Setting',
     items: [
-      { key: 'role-permission', title: '角色权限', path: '/system/roles', roles: ['admin'] },
-      { key: 'operation-log', title: '操作日志', path: '/system/logs', roles: ['admin', 'manager'] },
-      { key: 'api-key', title: 'API Key 管理', path: '/system/api-keys', roles: ['admin'] },
-      { key: 'system-notification', title: '消息中心', path: '/system/notification', roles: ['admin', 'manager'] },
-      { key: 'message-template', title: '消息模板', path: '/system/message-template', roles: ['admin', 'manager'] },
-      { key: 'approval-flow', title: '审批流程', path: '/system/approval-flow', roles: ['admin'] },
-      { key: 'system-employee', title: '员工管理', path: '/system/employee', roles: ['admin'] },
-      { key: 'system-department', title: '部门管理', path: '/system/department', roles: ['admin'] }
+      { key: 'role-permission', title: '角色权限', path: '/system/roles', roles: ['admin'], icon: 'User' },
+      { key: 'operation-log', title: '操作日志', path: '/system/logs', roles: ['admin', 'manager'], icon: 'Document' },
+      { key: 'api-key', title: 'API Key 管理', path: '/system/api-keys', roles: ['admin'], icon: 'Key' },
+      { key: 'system-notification', title: '消息中心', path: '/system/notification', roles: ['admin', 'manager'], icon: 'Bell' },
+      { key: 'message-template', title: '消息模板', path: '/system/message-template', roles: ['admin', 'manager'], icon: 'Message' },
+      { key: 'approval-flow', title: '审批流程', path: '/system/approval-flow', roles: ['admin'], icon: 'CircleCheck' },
+      { key: 'system-employee', title: '员工管理', path: '/system/employee', roles: ['admin'], icon: 'UserFilled' },
+      { key: 'system-department', title: '部门管理', path: '/system/department', roles: ['admin'], icon: 'OfficeBuilding' }
     ]
   },
   {
     key: 'return',
     title: '退件管理',
+    icon: 'RefreshLeft',
     items: [
-      { key: 'return-package', title: '退件管理', path: '/warehouse/return-package', roles: ['admin', 'manager', 'warehouse'] }
+      { key: 'return-package', title: '退件管理', path: '/warehouse/return-package', roles: ['admin', 'manager', 'warehouse'], icon: 'RefreshLeft' }
     ]
   }
-];
+];

+ 36 - 0
src/router/routes.ts

@@ -251,6 +251,42 @@ export const routes: RouteRecordRaw[] = [
         component: () => import('@/views/crm/SatisfactionView.vue'),
         meta: { title: '满意度评价', pageKey: 'crm-satisfaction', roles: ['admin', 'manager', 'customer_service'] }
       },
+      {
+        path: '/crm/ai-controller',
+        name: 'ai-controller',
+        component: () => import('@/views/crm/AIControllerView.vue'),
+        meta: { title: 'AI客服工作台', pageKey: 'ai-controller', roles: ['admin', 'manager', 'customer_service'] }
+      },
+      {
+        path: '/crm/knowledge-base',
+        name: 'knowledge-base',
+        component: () => import('@/views/crm/KnowledgeBaseView.vue'),
+        meta: { title: '知识库管理', pageKey: 'knowledge-base', roles: ['admin', 'manager', 'customer_service'] }
+      },
+      {
+        path: '/crm/auto-reply',
+        name: 'auto-reply',
+        component: () => import('@/views/crm/AutoReplyRuleView.vue'),
+        meta: { title: '自动回复规则', pageKey: 'auto-reply', roles: ['admin', 'manager', 'customer_service'] }
+      },
+      {
+        path: '/crm/chat-log',
+        name: 'chat-log',
+        component: () => import('@/views/crm/ChatLogView.vue'),
+        meta: { title: '对话日志', pageKey: 'chat-log', roles: ['admin', 'manager', 'customer_service'] }
+      },
+      {
+        path: '/crm/service-performance',
+        name: 'service-performance',
+        component: () => import('@/views/crm/ServicePerformanceView.vue'),
+        meta: { title: '客服绩效', pageKey: 'service-performance', roles: ['admin', 'manager'] }
+      },
+      {
+        path: '/crm/ai-channel-config',
+        name: 'ai-channel-config',
+        component: () => import('@/views/crm/ChannelConfigView.vue'),
+        meta: { title: 'AI渠道配置', pageKey: 'ai-channel-config', roles: ['admin', 'manager'] }
+      },
       {
         path: '/supplier/performance',
         name: 'supplier-performance',

+ 253 - 17
src/types/page.ts

@@ -143,51 +143,169 @@ export interface OrderItem {
   orderNo: string;
   channelOrderNo: string;
   channel: string;
-  buyer: string;
-  amount: string;
-  itemCount: number;
-  paymentStatus: string;
   orderStatus: string;
   shippingStatus: string;
-  warehouse: string;
+  paymentStatus: string;
   exceptionTag: string;
+  priority: string;
   createdAt: string;
-}
+  updatedAt: string;
+  paidAt: string;
+  shippedAt: string;
+  deliveredAt: string;
 
-export interface OrderDetail extends OrderItem {
-  receiver: string;
-  phone: string;
-  address: string;
-  payMethod: string;
-  payTime: string;
+  // Buyer Info
+  buyerId: string;
+  buyer: string;
+  buyerEmail: string;
+  buyerPhone: string;
+  buyerCountry: string;
+  buyerLevel: string;
+  buyerTags: string[];
+  buyerRegisterTime: string;
+  buyerOrderCount: number;
+  buyerTotalSpent: string;
+
+  // Receiver Info
+  receiverName: string;
+  receiverPhone: string;
+  receiverCountry: string;
+  receiverState: string;
+  receiverCity: string;
+  receiverDistrict: string;
+  receiverPostalCode: string;
+  receiverAddress: string;
+  receiverAddressLang: string;
+  latitude: number;
+  longitude: number;
+
+  // Payment Info
+  transactionId: string;
+  paymentMethod: string;
+  paymentTime: string;
+  currency: string;
+  exchangeRate: number;
+  orderAmount: string;
+  orderAmountCNY: string;
+  taxAmount: string;
+  shippingFee: string;
+  discountAmount: string;
+  couponCode: string;
+  couponDiscount: string;
+  actualPaid: string;
+  refundAmount: string;
+  refundStatus: string;
+
+  // Logistics Info
+  shippingMethod: string;
   carrier: string;
   trackingNo: string;
+  trackingUrl: string;
+  warehouse: string;
+  warehouseLocation: string;
+  estimatedDelivery: string;
+  weight: number;
+  length: number;
+  width: number;
+  height: number;
+
+  // Marketing Attribution
+  utmSource: string;
+  utmMedium: string;
+  utmCampaign: string;
+  utmContent: string;
+  utmTerm: string;
+  referrerUrl: string;
+  landingPage: string;
+  ip: string;
+  ipCountry: string;
+  device: string;
+  browser: string;
+  os: string;
+
+  // Order Relations
+  parentOrderId: string;
+  childOrderIds: string[];
+  mergeOrderId: string;
+  relatedOrderId: string;
+  originalOrderId: string;
+
+  // Customer Service
+  handler: string;
+  handlerGroup: string;
+  orderTags: string[];
+  internalRemark: string;
+  buyerRemark: string;
+
+  // Computed
+  itemCount: number;
+  amount: string;
   items: OrderProductItem[];
   timeline: StatusEvent[];
-  logs: string[];
-  remark: string;
+  logs: OperationLog[];
 }
 
 export interface OrderProductItem {
+  id: string;
+  productId: string;
+  skuId: string;
   sku: string;
-  title: string;
-  image: string;
+  productTitle: string;
+  productImage: string;
+  categoryId: string;
+  categoryName: string;
+  specs: ProductSpec[];
+  barcode: string;
   qty: number;
-  unitPrice: string;
+  price: string;
+  costPrice: string;
+  profit: string;
+  profitRate: number;
   subtotal: string;
+  weight: number;
+  available: number;
+  locked: number;
+  inTransit: number;
+  reservedQty: number;
+  shippedQty: number;
+  returnedQty: number;
+  giftFlag: boolean;
+}
+
+export interface ProductSpec {
+  specName: string;
+  specValue: string;
 }
 
 export interface StatusEvent {
   status: string;
   time: string;
+  title: string;
+  summary: string;
+  type: string;
   operator: string;
 }
 
+export interface OperationLog {
+  id: string;
+  time: string;
+  title: string;
+  content: string;
+  type: string;
+  operator: string;
+  operatorRole: string;
+}
+
+export interface OrderDetail extends OrderItem {
+  // Extended fields specific to detail view
+}
+
 /* ───── After Sale ───── */
 export interface AfterSaleItem {
   id: string;
   afterSaleNo: string;
   orderNo: string;
+  orderId?: string;
   buyer: string;
   type: string;
   amount: string;
@@ -547,6 +665,124 @@ export interface IQCItem {
   inspectTime: string;
 }
 
+/* ───── AI Customer Service ───── */
+export interface KnowledgeBaseItem {
+  id: string;
+  category: string;
+  categoryId: string;
+  keywords: string[];
+  question: string;
+  answer: string;
+  clicks: number;
+  aiScore: number;
+  status: 'enabled' | 'disabled';
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface KnowledgeCategory {
+  id: string;
+  name: string;
+  parentId?: string;
+  children?: KnowledgeCategory[];
+  count: number;
+}
+
+export interface ChatMessage {
+  id: string;
+  role: 'visitor' | 'ai' | 'agent' | 'system';
+  content: string;
+  timestamp: string;
+  attachments?: string[];
+}
+
+export interface ChatSession {
+  id: string;
+  visitorId: string;
+  visitorName: string;
+  visitorAvatar: string;
+  channel: string;
+  shopName: string;
+  messages: ChatMessage[];
+  aiHandled: boolean;
+  agentId?: string;
+  agentName?: string;
+  satisfaction?: number;
+  status: 'waiting' | 'ongoing' | 'ended';
+  source: string;
+  device: string;
+  location: string;
+  createdAt: string;
+  endedAt?: string;
+  lastMessageAt: string;
+}
+
+export interface OnlineVisitor {
+  id: string;
+  name: string;
+  avatar: string;
+  channel: string;
+  shopName: string;
+  status: 'browsing' | 'chatting' | 'ordering' | 'waiting';
+  currentPage: string;
+  visitDuration: number;
+  intent: string;
+  lastActivity: string;
+}
+
+export interface AutoReplyRule {
+  id: string;
+  name: string;
+  priority: number;
+  triggerType: 'keyword' | 'intent' | 'fallback' | 'greeting';
+  keywords: string[];
+  matchMode: 'exact' | 'contain' | 'regex';
+  responses: string[];
+  status: 'enabled' | 'disabled';
+  hitCount: number;
+  accuracy: number;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface ServicePerformance {
+  id: string;
+  agentId: string;
+  agentName: string;
+  agentAvatar: string;
+  department: string;
+  handleCount: number;
+  aiAssistCount: number;
+  avgResponseTime: number;
+  avgFirstResponseTime: number;
+  satisfaction: number;
+  solveRate: number;
+  date: string;
+}
+
+export interface ChatChannel {
+  id: string;
+  name: string;
+  code: string;
+  icon: string;
+  enabled: boolean;
+  robotName: string;
+  robotEnabled: boolean;
+  autoTransferThreshold: number;
+  priority: number;
+}
+
+export interface AIControllerStats {
+  totalVisitors: number;
+  onlineVisitors: number;
+  aiHandling: number;
+  humanHandling: number;
+  avgResponseTime: string;
+  todayChats: number;
+  todayAIResolve: number;
+  todaySatisfaction: number;
+}
+
 /* ───── CRM ───── */
 export interface TicketItem {
   id: string;

+ 673 - 0
src/views/crm/AIControllerView.vue

@@ -0,0 +1,673 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <div class="ai-stats-grid">
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">在线访客</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.onlineVisitors }}</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">AI接待中</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.aiHandling }}</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">人工接待中</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.humanHandling }}</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">平均响应</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.avgResponseTime }}</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22,4 12,14.01 9,11.01"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">今日AI解决</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.todayAIResolve }}</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">今日满意度</div>
+            <div class="stat-card__value" style="font-size:28px">{{ stats.todaySatisfaction }}%</div>
+          </div>
+        </article>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="chat-workspace">
+        <div class="visitor-list">
+          <div class="visitor-list__header">
+            <h3>在线访客</h3>
+            <el-badge :value="onlineVisitors.length" type="primary" />
+          </div>
+          <div class="visitor-list__search">
+            <el-input v-model="visitorSearch" placeholder="搜索访客..." prefix-icon="Search" clearable />
+          </div>
+          <div class="visitor-list__items">
+            <div 
+              v-for="visitor in filteredVisitors" 
+              :key="visitor.id"
+              class="visitor-item"
+              :class="{ 'visitor-item--active': currentVisitor?.id === visitor.id, 'visitor-item--chatting': visitor.status === 'chatting' }"
+              @click="selectVisitor(visitor)"
+            >
+              <div class="visitor-item__avatar">
+                <el-avatar :size="40" :src="visitor.avatar">{{ visitor.name[0] }}</el-avatar>
+                <span class="visitor-item__status" :class="`visitor-item__status--${visitor.status}`"></span>
+              </div>
+              <div class="visitor-item__info">
+                <div class="visitor-item__name">{{ visitor.name }}</div>
+                <div class="visitor-item__meta">
+                  <span class="visitor-item__channel">{{ visitor.channel }}</span>
+                  <span class="visitor-item__time">{{ formatDuration(visitor.visitDuration) }}</span>
+                </div>
+                <div class="visitor-item__page" :title="visitor.currentPage">{{ visitor.currentPage }}</div>
+              </div>
+              <el-tag v-if="visitor.status === 'chatting'" size="small" type="success">咨询中</el-tag>
+            </div>
+            <el-empty v-if="filteredVisitors.length === 0" description="暂无在线访客" />
+          </div>
+        </div>
+
+        <div class="chat-panel">
+          <template v-if="currentVisitor">
+            <div class="chat-panel__header">
+              <div class="chat-panel__visitor-info">
+                <el-avatar :size="36" :src="currentVisitor.avatar">{{ currentVisitor.name[0] }}</el-avatar>
+                <div>
+                  <div class="chat-panel__visitor-name">{{ currentVisitor.name }}</div>
+                  <div class="chat-panel__visitor-meta">
+                    <el-tag size="small">{{ currentVisitor.channel }}</el-tag>
+                    <span class="chat-panel__location">{{ currentVisitor.location }}</span>
+                  </div>
+                </div>
+              </div>
+              <div class="chat-panel__actions">
+                <el-select v-model="chatMode" size="small" style="width:100px">
+                  <el-option label="AI模式" value="ai" />
+                  <el-option label="人工模式" value="human" />
+                </el-select>
+                <el-button size="small" @click="transferToAgent">转人工</el-button>
+                <el-button size="small" type="danger" @click="endChat">结束会话</el-button>
+              </div>
+            </div>
+
+            <div class="chat-panel__messages" ref="messageContainer">
+              <div 
+                v-for="msg in currentMessages" 
+                :key="msg.id"
+                class="chat-message"
+                :class="`chat-message--${msg.role}`"
+              >
+                <el-avatar v-if="msg.role !== 'visitor'" :size="32" class="chat-message__avatar">
+                  {{ msg.role === 'ai' ? '🤖' : '👤' }}
+                </el-avatar>
+                <div class="chat-message__content">
+                  <div class="chat-message__bubble">{{ msg.content }}</div>
+                  <div class="chat-message__time">{{ msg.timestamp }}</div>
+                </div>
+                <el-avatar v-if="msg.role === 'visitor'" :size="32" class="chat-message__avatar">
+                  {{ currentVisitor.name[0] }}
+                </el-avatar>
+              </div>
+            </div>
+
+            <div class="chat-panel__input">
+              <div class="quick-replies">
+                <el-tag v-for="reply in quickReplies" :key="reply" @click="sendQuickReply(reply)" class="quick-reply">{{ reply }}</el-tag>
+              </div>
+              <div class="input-row">
+                <el-input v-model="newMessage" placeholder="输入消息..." @keyup.enter="sendMessage" />
+                <el-button type="primary" @click="sendMessage" :disabled="!newMessage.trim()">发送</el-button>
+              </div>
+            </div>
+          </template>
+          <el-empty v-else description="选择一位访客开始对话" />
+        </div>
+
+        <div class="session-info">
+          <div class="session-info__header">
+            <h3>会话信息</h3>
+          </div>
+          <template v-if="currentVisitor">
+            <div class="session-info__item">
+              <span class="session-info__label">访客ID</span>
+              <span class="session-info__value">{{ currentVisitor.id }}</span>
+            </div>
+            <div class="session-info__item">
+              <span class="session-info__label">设备</span>
+              <span class="session-info__value">{{ currentVisitor.device }}</span>
+            </div>
+            <div class="session-info__item">
+              <span class="session-info__label">来源</span>
+              <span class="session-info__value">{{ currentVisitor.source }}</span>
+            </div>
+            <div class="session-info__item">
+              <span class="session-info__label">当前页面</span>
+              <span class="session-info__value" :title="currentVisitor.currentPage">{{ truncate(currentVisitor.currentPage, 20) }}</span>
+            </div>
+            <div class="session-info__item">
+              <span class="session-info__label">访问时长</span>
+              <span class="session-info__value">{{ formatDuration(currentVisitor.visitDuration) }}</span>
+            </div>
+            <div class="session-info__item">
+              <span class="session-info__label">咨询意图</span>
+              <el-tag size="small">{{ currentVisitor.intent || '未识别' }}</el-tag>
+            </div>
+          </template>
+          <el-empty v-else description="暂无会话信息" />
+        </div>
+      </div>
+    </section>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, nextTick } from 'vue';
+import { ElMessage } from 'element-plus';
+import type { OnlineVisitor, ChatMessage, ChatSession, AIControllerStats } from '@/types/page';
+
+const stats = ref<AIControllerStats>({
+  totalVisitors: 89,
+  onlineVisitors: 23,
+  aiHandling: 18,
+  humanHandling: 5,
+  avgResponseTime: '1.2s',
+  todayChats: 156,
+  todayAIResolve: 89,
+  todaySatisfaction: 96.5
+});
+
+const onlineVisitors = ref<OnlineVisitor[]>([
+  { id: 'V001', name: '张小姐', avatar: '', channel: '淘宝', shopName: '旗舰店', status: 'chatting', currentPage: '/product/detail/12345', visitDuration: 320, intent: '物流查询', lastActivity: '刚刚' },
+  { id: 'V002', name: '李先生', avatar: '', channel: '京东', shopName: '自营店', status: 'browsing', currentPage: '/cart', visitDuration: 180, intent: '价格咨询', lastActivity: '5分钟前' },
+  { id: 'V003', name: '王女士', avatar: '', channel: '拼多多', shopName: '官方店', status: 'waiting', currentPage: '/product/list', visitDuration: 420, intent: '产品推荐', lastActivity: '1分钟前' },
+  { id: 'V004', name: '赵先生', avatar: '', channel: '独立站', shopName: '官网', status: 'ordering', currentPage: '/checkout', visitDuration: 95, intent: '支付问题', lastActivity: '刚刚' },
+  { id: 'V005', name: '陈小姐', avatar: '', channel: '天猫', shopName: '旗舰店', status: 'chatting', currentPage: '/user/order', visitDuration: 560, intent: '退换货', lastActivity: '刚刚' },
+  { id: 'V006', name: '周先生', avatar: '', channel: '抖音', shopName: '直播间', status: 'browsing', currentPage: '/product/detail/67890', visitDuration: 45, intent: '规格咨询', lastActivity: '3分钟前' }
+]);
+
+const currentVisitor = ref<OnlineVisitor | null>(null);
+const currentMessages = ref<ChatMessage[]>([]);
+const newMessage = ref('');
+const visitorSearch = ref('');
+const chatMode = ref<'ai' | 'human'>('ai');
+const messageContainer = ref<HTMLElement | null>(null);
+
+const quickReplies = ['您好,请问有什么可以帮您?', '您的订单已发货,预计3-5天送达', '请提供您的订单号,我帮您查询', '感谢您的咨询,祝您购物愉快!'];
+
+const filteredVisitors = computed(() => {
+  if (!visitorSearch.value) return onlineVisitors.value;
+  const search = visitorSearch.value.toLowerCase();
+  return onlineVisitors.value.filter(v => 
+    v.name.toLowerCase().includes(search) || 
+    v.channel.toLowerCase().includes(search)
+  );
+});
+
+const selectVisitor = (visitor: OnlineVisitor) => {
+  currentVisitor.value = visitor;
+  if (visitor.status !== 'chatting') {
+    visitor.status = 'chatting';
+  }
+  loadMessages(visitor.id);
+};
+
+const loadMessages = (visitorId: string) => {
+  currentMessages.value = [
+    { id: '1', role: 'visitor', content: '你好,我想问一下我的订单什么时候能到?', timestamp: '14:23' },
+    { id: '2', role: 'ai', content: '您好!我是智能客服小C,请问您的订单号是多少?', timestamp: '14:23' },
+    { id: '3', role: 'visitor', content: '订单号是 DD20240115001', timestamp: '14:24' },
+    { id: '4', role: 'ai', content: '查询到您的订单已于1月15日发货,预计1月18日-20日送达。快递单号:SF1234567890', timestamp: '14:24' },
+    { id: '5', role: 'visitor', content: '好的,谢谢!', timestamp: '14:25' }
+  ];
+  scrollToBottom();
+};
+
+const sendMessage = () => {
+  if (!newMessage.value.trim() || !currentVisitor.value) return;
+  
+  const msg: ChatMessage = {
+    id: Date.now().toString(),
+    role: 'agent',
+    content: newMessage.value,
+    timestamp: new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
+  };
+  currentMessages.value.push(msg);
+  newMessage.value = '';
+  
+  setTimeout(() => {
+    const reply: ChatMessage = {
+      id: (Date.now() + 1).toString(),
+      role: 'ai',
+      content: chatMode.value === 'ai' ? '感谢您的咨询,如有其他问题请随时联系我!' : '人工客服接入中,请稍候...',
+      timestamp: new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
+    };
+    currentMessages.value.push(reply);
+    scrollToBottom();
+  }, 1000);
+  
+  scrollToBottom();
+};
+
+const sendQuickReply = (reply: string) => {
+  newMessage.value = reply;
+  sendMessage();
+};
+
+const transferToAgent = () => {
+  ElMessage.success('已转接至人工客服');
+  chatMode.value = 'human';
+};
+
+const endChat = () => {
+  ElMessage.success('会话已结束');
+  if (currentVisitor.value) {
+    currentVisitor.value.status = 'browsing';
+  }
+  currentVisitor.value = null;
+  currentMessages.value = [];
+};
+
+const scrollToBottom = () => {
+  nextTick(() => {
+    if (messageContainer.value) {
+      messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
+    }
+  });
+};
+
+const formatDuration = (seconds: number): string => {
+  if (seconds < 60) return `${seconds}秒`;
+  if (seconds < 3600) return `${Math.floor(seconds / 60)}分`;
+  return `${Math.floor(seconds / 3600)}时${Math.floor((seconds % 3600) / 60)}分`;
+};
+
+const truncate = (str: string, len: number): string => {
+  return str.length > len ? str.substring(0, len) + '...' : str;
+};
+
+onMounted(() => {
+  if (onlineVisitors.value.length > 0) {
+    selectVisitor(onlineVisitors.value[0]);
+  }
+});
+</script>
+
+<style scoped>
+.ai-stats-grid {
+  display: grid;
+  grid-template-columns: repeat(6, 1fr);
+  gap: 16px;
+}
+
+.stat-card {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 16px;
+  background: #fafafa;
+  border-radius: 12px;
+}
+
+.stat-card__icon {
+  width: 48px;
+  height: 48px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+.stat-card__icon svg {
+  width: 24px;
+  height: 24px;
+}
+
+.stat-card__content {
+  flex: 1;
+  min-width: 0;
+}
+
+.stat-card__label {
+  font-size: 12px;
+  color: #666;
+  margin-bottom: 4px;
+}
+
+.stat-card__value {
+  font-size: 24px;
+  font-weight: 700;
+  color: #333;
+}
+
+.chat-workspace {
+  display: grid;
+  grid-template-columns: 280px 1fr 240px;
+  gap: 16px;
+  height: calc(100vh - 320px);
+  min-height: 500px;
+}
+
+.visitor-list {
+  background: #fafafa;
+  border-radius: 12px;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+}
+
+.visitor-list__header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 12px;
+}
+
+.visitor-list__header h3 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+
+.visitor-list__search {
+  margin-bottom: 12px;
+}
+
+.visitor-list__items {
+  flex: 1;
+  overflow-y: auto;
+}
+
+.visitor-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 10px;
+  padding: 12px;
+  border-radius: 10px;
+  cursor: pointer;
+  transition: all 0.2s;
+  margin-bottom: 8px;
+}
+
+.visitor-item:hover {
+  background: rgba(15, 118, 110, 0.08);
+}
+
+.visitor-item--active {
+  background: rgba(15, 118, 110, 0.14);
+}
+
+.visitor-item--chatting {
+  border-left: 3px solid #11998e;
+}
+
+.visitor-item__avatar {
+  position: relative;
+  flex-shrink: 0;
+}
+
+.visitor-item__status {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  border: 2px solid #fff;
+}
+
+.visitor-item__status--browsing { background: #909399; }
+.visitor-item__status--chatting { background: #67c23a; }
+.visitor-item__status--ordering { background: #e6a23c; }
+.visitor-item__status--waiting { background: #f56c6c; }
+
+.visitor-item__info {
+  flex: 1;
+  min-width: 0;
+}
+
+.visitor-item__name {
+  font-weight: 600;
+  font-size: 14px;
+  margin-bottom: 2px;
+}
+
+.visitor-item__meta {
+  display: flex;
+  gap: 8px;
+  font-size: 12px;
+  color: #999;
+  margin-bottom: 4px;
+}
+
+.visitor-item__channel {
+  color: var(--cb-primary);
+}
+
+.visitor-item__page {
+  font-size: 11px;
+  color: #999;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.chat-panel {
+  background: #fafafa;
+  border-radius: 12px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.chat-panel__header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 16px;
+  border-bottom: 1px solid #eee;
+  background: #fff;
+}
+
+.chat-panel__visitor-info {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.chat-panel__visitor-name {
+  font-weight: 600;
+  font-size: 15px;
+}
+
+.chat-panel__visitor-meta {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-top: 4px;
+}
+
+.chat-panel__location {
+  font-size: 12px;
+  color: #999;
+}
+
+.chat-panel__actions {
+  display: flex;
+  gap: 8px;
+}
+
+.chat-panel__messages {
+  flex: 1;
+  overflow-y: auto;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.chat-message {
+  display: flex;
+  gap: 10px;
+  max-width: 75%;
+}
+
+.chat-message--visitor {
+  align-self: flex-end;
+  flex-direction: row-reverse;
+}
+
+.chat-message--ai,
+.chat-message--agent {
+  align-self: flex-start;
+}
+
+.chat-message__content {
+  display: flex;
+  flex-direction: column;
+}
+
+.chat-message--visitor .chat-message__content {
+  align-items: flex-end;
+}
+
+.chat-message__bubble {
+  padding: 10px 14px;
+  border-radius: 16px;
+  font-size: 14px;
+  line-height: 1.5;
+}
+
+.chat-message--visitor .chat-message__bubble {
+  background: var(--cb-primary);
+  color: #fff;
+  border-bottom-right-radius: 4px;
+}
+
+.chat-message--ai .chat-message__bubble {
+  background: #fff;
+  color: #333;
+  border-bottom-left-radius: 4px;
+  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
+}
+
+.chat-message--agent .chat-message__bubble {
+  background: #e8f4ff;
+  color: #333;
+  border-bottom-left-radius: 4px;
+}
+
+.chat-message__time {
+  font-size: 11px;
+  color: #999;
+  margin-top: 4px;
+}
+
+.chat-panel__input {
+  padding: 16px;
+  border-top: 1px solid #eee;
+  background: #fff;
+}
+
+.quick-replies {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-bottom: 12px;
+}
+
+.quick-reply {
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.input-row {
+  display: flex;
+  gap: 8px;
+}
+
+.input-row .el-input {
+  flex: 1;
+}
+
+.session-info {
+  background: #fafafa;
+  border-radius: 12px;
+  padding: 16px;
+}
+
+.session-info__header {
+  margin-bottom: 16px;
+}
+
+.session-info__header h3 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+
+.session-info__item {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+  padding: 10px 0;
+  border-bottom: 1px solid #eee;
+}
+
+.session-info__item:last-child {
+  border-bottom: none;
+}
+
+.session-info__label {
+  font-size: 12px;
+  color: #999;
+}
+
+.session-info__value {
+  font-size: 13px;
+  color: #333;
+}
+
+@media (max-width: 1200px) {
+  .ai-stats-grid {
+    grid-template-columns: repeat(3, 1fr);
+  }
+  
+  .chat-workspace {
+    grid-template-columns: 1fr;
+    height: auto;
+  }
+  
+  .session-info {
+    display: none;
+  }
+}
+</style>

+ 475 - 0
src/views/crm/AutoReplyRuleView.vue

@@ -0,0 +1,475 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <el-form :model="filters" inline class="filter-form">
+        <el-form-item label="规则名称">
+          <el-input v-model="filters.name" placeholder="搜索规则名称" clearable style="width:160px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="触发类型">
+          <el-select v-model="filters.triggerType" placeholder="全部" clearable style="width:130px">
+            <el-option label="关键词" value="keyword" />
+            <el-option label="意图识别" value="intent" />
+            <el-option label="寒暄" value="greeting" />
+            <el-option label="兜底回复" value="fallback" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-select v-model="filters.status" placeholder="全部" clearable style="width:120px">
+            <el-option label="已启用" value="enabled" />
+            <el-option label="已禁用" value="disabled" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="loadData">查询</el-button>
+          <el-button @click="resetFilters">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </section>
+
+    <section class="glass-card section-card" style="padding:12px 24px">
+      <div class="table-toolbar" style="margin-bottom:0">
+        <div class="chip-list">
+          <el-button type="primary" @click="openCreate">新建规则</el-button>
+          <el-button type="success" @click="saveFlow">保存流程</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="rule-layout">
+        <div class="rule-list">
+          <div class="rule-list__header">
+            <h3>规则列表</h3>
+            <span class="rule-list__hint">拖拽调整优先级</span>
+          </div>
+          <div class="rule-items">
+            <div 
+              v-for="(rule, index) in filteredRules" 
+              :key="rule.id"
+              class="rule-item"
+              :class="{ 'rule-item--active': currentRule?.id === rule.id, 'rule-item--disabled': rule.status === 'disabled' }"
+              @click="selectRule(rule)"
+            >
+              <div class="rule-item__priority">
+                <el-tag size="small" :type="index === 0 ? 'danger' : index < 3 ? 'warning' : 'info'">
+                  {{ index === 0 ? '最高' : `优先${index + 1}` }}
+                </el-tag>
+              </div>
+              <div class="rule-item__info">
+                <div class="rule-item__name">{{ rule.name }}</div>
+                <div class="rule-item__meta">
+                  <el-tag size="small" :type="triggerTypeTag(rule.triggerType)">{{ triggerTypeLabel(rule.triggerType) }}</el-tag>
+                  <span class="rule-item__hit">命中 {{ rule.hitCount }} 次</span>
+                </div>
+              </div>
+              <div class="rule-item__status">
+                <el-switch 
+                  :model-value="rule.status === 'enabled'" 
+                  @change="toggleStatus(rule, $event)"
+                  @click.stop
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="rule-detail">
+          <template v-if="currentRule">
+            <div class="rule-detail__header">
+              <h3>{{ isEdit ? '编辑规则' : '规则详情' }}</h3>
+              <el-button v-if="!isEdit" type="primary" size="small" @click="isEdit = true">编辑</el-button>
+              <el-button v-if="isEdit" size="small" @click="cancelEdit">取消</el-button>
+              <el-button v-if="isEdit" type="success" size="small" @click="saveRule">保存</el-button>
+            </div>
+
+            <el-form :model="editForm" label-width="100px" class="rule-form">
+              <el-form-item label="规则名称" required>
+                <el-input v-model="editForm.name" :disabled="!isEdit" placeholder="请输入规则名称" />
+              </el-form-item>
+
+              <el-form-item label="触发类型" required>
+                <el-select v-model="editForm.triggerType" :disabled="!isEdit" style="width:100%">
+                  <el-option label="关键词匹配" value="keyword" />
+                  <el-option label="意图识别" value="intent" />
+                  <el-option label="寒暄回复" value="greeting" />
+                  <el-option label="兜底回复" value="fallback" />
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="匹配模式">
+                <el-radio-group v-model="editForm.matchMode" :disabled="!isEdit">
+                  <el-radio label="contain">包含任意关键词</el-radio>
+                  <el-radio label="exact">完全匹配</el-radio>
+                  <el-radio label="regex">正则表达式</el-radio>
+                </el-radio-group>
+              </el-form-item>
+
+              <el-form-item label="关键词" v-if="editForm.triggerType === 'keyword'" required>
+                <el-select v-model="editForm.keywords" multiple filterable allow-create :disabled="!isEdit" placeholder="输入关键词后回车" style="width:100%">
+                  <el-option v-for="kw in editForm.keywords" :key="kw" :label="kw" :value="kw" />
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="回复内容" required>
+                <el-input v-model="editForm.responses[0]" type="textarea" :rows="4" :disabled="!isEdit" placeholder="请输入回复内容" />
+              </el-form-item>
+
+              <el-form-item label="多轮回复" v-if="editForm.responses.length > 1">
+                <div class="multi-response">
+                  <div v-for="(resp, idx) in editForm.responses.slice(1)" :key="idx" class="multi-response__item">
+                    <span class="multi-response__label">追问{{ idx + 1 }}:</span>
+                    <el-input v-model="editForm.responses[idx + 1]" :disabled="!isEdit" />
+                  </div>
+                </div>
+                <el-button v-if="isEdit" size="small" @click="addResponse" style="margin-top:8px">添加追问</el-button>
+              </el-form-item>
+
+              <el-form-item label="统计信息">
+                <div class="rule-stats">
+                  <div class="rule-stats__item">
+                    <span class="rule-stats__label">命中次数</span>
+                    <span class="rule-stats__value">{{ currentRule.hitCount }}</span>
+                  </div>
+                  <div class="rule-stats__item">
+                    <span class="rule-stats__label">准确率</span>
+                    <span class="rule-stats__value">{{ currentRule.accuracy }}%</span>
+                  </div>
+                </div>
+              </el-form-item>
+
+              <el-form-item label="测试">
+                <div class="test-section">
+                  <el-input v-model="testMessage" placeholder="输入测试消息" style="width:200px" />
+                  <el-button @click="testRule" :loading="testing">测试</el-button>
+                </div>
+                <div v-if="testResult" class="test-result">
+                  <span class="test-result__label">回复:</span>
+                  <span class="test-result__content">{{ testResult }}</span>
+                </div>
+              </el-form-item>
+            </el-form>
+          </template>
+          <el-empty v-else description="请选择一条规则查看详情" />
+        </div>
+      </div>
+    </section>
+
+    <el-dialog v-model="dialogVisible" title="新建规则" width="600px">
+      <el-form :model="form" label-width="100px">
+        <el-form-item label="规则名称" required>
+          <el-input v-model="form.name" placeholder="请输入规则名称" />
+        </el-form-item>
+        <el-form-item label="触发类型" required>
+          <el-select v-model="form.triggerType" style="width:100%">
+            <el-option label="关键词匹配" value="keyword" />
+            <el-option label="意图识别" value="intent" />
+            <el-option label="寒暄回复" value="greeting" />
+            <el-option label="兜底回复" value="fallback" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="匹配模式">
+          <el-radio-group v-model="form.matchMode">
+            <el-radio label="contain">包含任意关键词</el-radio>
+            <el-radio label="exact">完全匹配</el-radio>
+            <el-radio label="regex">正则表达式</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="关键词" v-if="form.triggerType === 'keyword'" required>
+          <el-select v-model="form.keywords" multiple filterable allow-create placeholder="输入关键词后回车" style="width:100%">
+            <el-option v-for="kw in form.keywords" :key="kw" :label="kw" :value="kw" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="回复内容" required>
+          <el-input v-model="form.responses[0]" type="textarea" :rows="3" placeholder="请输入回复内容" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="createRule">创建</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import type { AutoReplyRule } from '@/types/page';
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const isEdit = ref(false);
+const currentRule = ref<AutoReplyRule | null>(null);
+const testMessage = ref('');
+const testResult = ref('');
+const testing = ref(false);
+
+const filters = ref({ name: '', triggerType: '', status: '' });
+
+const rules = ref<AutoReplyRule[]>([
+  { id: 'R001', name: '物流查询', priority: 1, triggerType: 'keyword', keywords: ['物流', '快递', '发货', '到哪了', '什么时候到'], matchMode: 'contain', responses: ['您好,您的订单已在运输中,预计2-3天送达。'], status: 'enabled', hitCount: 1567, accuracy: 94, createdAt: '2024-01-01', updatedAt: '2024-01-15' },
+  { id: 'R002', name: '退换货咨询', priority: 2, triggerType: 'keyword', keywords: ['退货', '换货', '退款', '售后'], matchMode: 'contain', responses: ['本店支持7天无理由退货(定制商品除外)。请登录账号申请退货退款。'], status: 'enabled', hitCount: 892, accuracy: 91, createdAt: '2024-01-02', updatedAt: '2024-01-14' },
+  { id: 'R003', name: '支付问题', priority: 3, triggerType: 'keyword', keywords: ['支付', '付款', '银行卡', '支付宝', '微信'], matchMode: 'contain', responses: ['我们支持支付宝、微信支付、银行卡等多种支付方式。'], status: 'enabled', hitCount: 654, accuracy: 88, createdAt: '2024-01-03', updatedAt: '2024-01-13' },
+  { id: 'R004', name: '产品推荐', priority: 4, triggerType: 'intent', keywords: [], matchMode: 'contain', responses: ['根据您的需求,为您推荐以下热销商品...'], status: 'enabled', hitCount: 432, accuracy: 82, createdAt: '2024-01-05', updatedAt: '2024-01-12' },
+  { id: 'R005', name: '问候寒暄', priority: 5, triggerType: 'greeting', keywords: ['你好', '您好', 'hi', 'hello', '在吗'], matchMode: 'contain', responses: ['您好!很高兴为您服务。请问有什么可以帮助您的?'], status: 'enabled', hitCount: 2341, accuracy: 96, createdAt: '2024-01-01', updatedAt: '2024-01-10' },
+  { id: 'R006', name: '感谢回复', priority: 6, triggerType: 'greeting', keywords: ['谢谢', '感谢', '辛苦了'], matchMode: 'contain', responses: ['不客气!很高兴能帮到您,祝您购物愉快!'], status: 'enabled', hitCount: 1234, accuracy: 98, createdAt: '2024-01-01', updatedAt: '2024-01-08' },
+  { id: 'R007', name: '未知问题兜底', priority: 99, triggerType: 'fallback', keywords: [], matchMode: 'contain', responses: ['抱歉,您的问题我未能理解。请问您可以详细描述一下吗?您也可以联系人工客服获得更专业的帮助。'], status: 'enabled', hitCount: 567, accuracy: 75, createdAt: '2024-01-01', updatedAt: '2024-01-05' },
+  { id: 'R008', name: '优惠券咨询', priority: 7, triggerType: 'keyword', keywords: ['优惠', '优惠券', '打折', '促销'], matchMode: 'contain', responses: ['当前有满199减20的优惠券可领取,是否需要我帮您跳转领取?'], status: 'disabled', hitCount: 234, accuracy: 85, createdAt: '2024-01-06', updatedAt: '2024-01-07' }
+]);
+
+const filteredRules = computed(() => {
+  return rules.value.filter(rule => {
+    if (filters.value.name && !rule.name.includes(filters.value.name)) return false;
+    if (filters.value.triggerType && rule.triggerType !== filters.value.triggerType) return false;
+    if (filters.value.status && rule.status !== filters.value.status) return false;
+    return true;
+  }).sort((a, b) => a.priority - b.priority);
+});
+
+const editForm = ref<Partial<AutoReplyRule>>({});
+const form = ref<Partial<AutoReplyRule>>({ keywords: [], responses: [''], matchMode: 'contain', triggerType: 'keyword' });
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { name: '', triggerType: '', status: '' }; };
+
+const triggerTypeLabel = (type: string) => {
+  const map: Record<string, string> = { keyword: '关键词', intent: '意图', greeting: '寒暄', fallback: '兜底' };
+  return map[type] || type;
+};
+
+const triggerTypeTag = (type: string) => {
+  const map: Record<string, string> = { keyword: 'primary', intent: 'success', greeting: 'warning', fallback: 'info' };
+  return map[type] || '';
+};
+
+const selectRule = (rule: AutoReplyRule) => {
+  currentRule.value = rule;
+  editForm.value = { ...rule, responses: [...rule.responses] };
+  isEdit.value = false;
+  testResult.value = '';
+};
+
+const toggleStatus = (rule: AutoReplyRule, val: boolean) => {
+  rule.status = val ? 'enabled' : 'disabled';
+  ElMessage.success(`已${val ? '启用' : '禁用'}`);
+};
+
+const openCreate = () => {
+  form.value = { keywords: [], responses: [''], matchMode: 'contain', triggerType: 'keyword' };
+  dialogVisible.value = true;
+};
+
+const createRule = () => {
+  if (!form.value.name || !form.value.responses?.[0]) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  rules.value.push({
+    id: 'R' + Date.now(),
+    priority: rules.value.length + 1,
+    hitCount: 0,
+    accuracy: 0,
+    status: 'enabled',
+    createdAt: new Date().toISOString().split('T')[0],
+    updatedAt: new Date().toISOString().split('T')[0],
+    ...form.value
+  } as AutoReplyRule);
+  ElMessage.success('创建成功');
+  dialogVisible.value = false;
+};
+
+const saveRule = () => {
+  if (!editForm.value.name || !editForm.value.responses?.[0]) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  const idx = rules.value.findIndex(r => r.id === currentRule.value?.id);
+  if (idx !== -1) {
+    rules.value[idx] = { ...rules.value[idx], ...editForm.value, updatedAt: new Date().toISOString().split('T')[0] } as AutoReplyRule;
+    currentRule.value = rules.value[idx];
+  }
+  isEdit.value = false;
+  ElMessage.success('保存成功');
+};
+
+const cancelEdit = () => {
+  if (currentRule.value) {
+    editForm.value = { ...currentRule.value, responses: [...currentRule.value.responses] };
+  }
+  isEdit.value = false;
+};
+
+const addResponse = () => {
+  if (!editForm.value.responses) editForm.value.responses = [];
+  editForm.value.responses.push('');
+};
+
+const testRule = () => {
+  if (!testMessage.value) {
+    ElMessage.warning('请输入测试消息');
+    return;
+  }
+  testing.value = true;
+  setTimeout(() => {
+    if (currentRule.value?.triggerType === 'fallback') {
+      testResult.value = currentRule.value.responses[0];
+    } else if (testMessage.value.includes('物流') || testMessage.value.includes('快递')) {
+      testResult.value = rules.value[0].responses[0];
+    } else if (testMessage.value.includes('你好') || testMessage.value.includes('您好')) {
+      testResult.value = rules.value[4].responses[0];
+    } else {
+      testResult.value = rules.value.find(r => r.triggerType === 'fallback')?.responses[0] || '抱歉,未匹配到任何规则';
+    }
+    testing.value = false;
+  }, 500);
+};
+
+const saveFlow = () => { ElMessage.success('流程保存成功'); };
+
+onMounted(() => {
+  if (filteredRules.value.length > 0) {
+    selectRule(filteredRules.value[0]);
+  }
+});
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+.rule-layout {
+  display: grid;
+  grid-template-columns: 320px 1fr;
+  gap: 16px;
+  min-height: 600px;
+}
+.rule-list {
+  background: #fafafa;
+  border-radius: 12px;
+  padding: 16px;
+}
+.rule-list__header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+}
+.rule-list__header h3 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+.rule-list__hint {
+  font-size: 12px;
+  color: #999;
+}
+.rule-items {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+.rule-item {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 12px;
+  background: #fff;
+  border-radius: 10px;
+  cursor: pointer;
+  transition: all 0.2s;
+  border: 2px solid transparent;
+}
+.rule-item:hover {
+  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
+}
+.rule-item--active {
+  border-color: var(--cb-primary);
+  background: rgba(15, 118, 110, 0.05);
+}
+.rule-item--disabled {
+  opacity: 0.6;
+}
+.rule-item__info {
+  flex: 1;
+  min-width: 0;
+}
+.rule-item__name {
+  font-weight: 600;
+  font-size: 14px;
+  margin-bottom: 4px;
+}
+.rule-item__meta {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.rule-item__hit {
+  font-size: 12px;
+  color: #999;
+}
+.rule-detail {
+  background: #fafafa;
+  border-radius: 12px;
+  padding: 16px;
+}
+.rule-detail__header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+.rule-detail__header h3 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+.rule-form {
+  max-width: 600px;
+}
+.multi-response__item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+}
+.multi-response__label {
+  font-size: 13px;
+  color: #666;
+  white-space: nowrap;
+}
+.rule-stats {
+  display: flex;
+  gap: 24px;
+}
+.rule-stats__item {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+.rule-stats__label {
+  font-size: 12px;
+  color: #999;
+}
+.rule-stats__value {
+  font-size: 18px;
+  font-weight: 600;
+  color: var(--cb-primary);
+}
+.test-section {
+  display: flex;
+  gap: 8px;
+}
+.test-result {
+  margin-top: 12px;
+  padding: 12px;
+  background: #fff;
+  border-radius: 8px;
+}
+.test-result__label {
+  font-size: 13px;
+  color: #666;
+}
+.test-result__content {
+  font-size: 14px;
+  color: #333;
+}
+</style>

+ 291 - 0
src/views/crm/ChannelConfigView.vue

@@ -0,0 +1,291 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card" style="padding:12px 24px">
+      <div class="table-toolbar" style="margin-bottom:0">
+        <div class="chip-list">
+          <el-button type="primary" @click="openCreate">新增渠道</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="channels" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="name" label="渠道名称" width="150">
+          <template #default="{ row }">
+            <div style="display:flex;align-items:center;gap:8px">
+              <span class="channel-icon" :style="{ background: channelColors[row.code] || '#666' }">{{ row.name[0] }}</span>
+              <span>{{ row.name }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="code" label="渠道代码" width="120">
+          <template #default="{ row }">
+            <el-tag size="small" type="info">{{ row.code }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="priority" label="优先级" width="80" align="center">
+          <template #default="{ row }">
+            <el-tag size="small" :type="row.priority <= 3 ? 'danger' : row.priority <= 5 ? 'warning' : 'info'">
+              {{ row.priority }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="robotName" label="机器人名称" width="130" />
+        <el-table-column prop="enabled" label="渠道状态" width="100" align="center">
+          <template #default="{ row }">
+            <el-switch :model-value="row.enabled" @change="toggleEnabled(row, $event)" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="robotEnabled" label="机器人" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="row.robotEnabled ? 'success' : 'info'" size="small">
+              {{ row.robotEnabled ? '已启用' : '已禁用' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="autoTransferThreshold" label="转人工阈值" width="110" align="center">
+          <template #default="{ row }">
+            {{ row.autoTransferThreshold }}轮
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+            <el-button link :type="row.robotEnabled ? 'warning' : 'success'" @click="toggleRobot(row)">
+              {{ row.robotEnabled ? '停用机器人' : '启用机器人' }}
+            </el-button>
+            <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="section-header">
+        <h3>渠道接入说明</h3>
+      </div>
+      <div class="channel-guide">
+        <el-collapse>
+          <el-collapse-item title="淘宝渠道接入" name="taobao">
+            <div class="guide-content">
+              <p><strong>接入步骤:</strong></p>
+              <ol>
+                <li>在淘宝开放平台创建应用</li>
+                <li>获取 App Key 和 App Secret</li>
+                <li>在系统配置中填入凭证</li>
+                <li>配置消息回调地址</li>
+                <li>完成权限授权</li>
+              </ol>
+              <p><strong>回调地址:</strong><el-tag>https://your-domain.com/api/crm/callback/taobao</el-tag></p>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="京东渠道接入" name="jd">
+            <div class="guide-content">
+              <p><strong>接入步骤:</strong></p>
+              <ol>
+                <li>在京东商家开放平台注册</li>
+                <li>创建推送应用获取 key</li>
+                <li>配置系统对接参数</li>
+                <li>开启消息推送服务</li>
+              </ol>
+              <p><strong>回调地址:</strong><el-tag>https://your-domain.com/api/crm/callback/jd</el-tag></p>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="独立站渠道接入" name="website">
+            <div class="guide-content">
+              <p><strong>接入方式:</strong></p>
+              <ol>
+                <li>使用 Web Widget 嵌入客服组件</li>
+                <li>或通过 JS SDK 对接</li>
+                <li>配置网站域名白名单</li>
+              </ol>
+              <p><strong>嵌入代码:</strong></p>
+              <pre class="code-block">&lt;script src="https://your-domain.com/widget.js"&gt;&lt;/script&gt;
+&lt;script&gt;
+  CBOS.init({
+    websiteId: 'your-website-id',
+    theme: 'light'
+  });
+&lt;/script&gt;</pre>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+    </section>
+
+    <el-dialog v-model="dialogVisible" :title="isEdit ? '编辑渠道' : '新增渠道'" width="500px" @closed="resetForm">
+      <el-form :model="form" label-width="100px">
+        <el-form-item label="渠道名称" required>
+          <el-input v-model="form.name" placeholder="请输入渠道名称" />
+        </el-form-item>
+        <el-form-item label="渠道代码" required>
+          <el-input v-model="form.code" placeholder="如:taobao、jd" :disabled="isEdit" />
+        </el-form-item>
+        <el-form-item label="优先级">
+          <el-input-number v-model="form.priority" :min="1" :max="99" />
+          <span style="margin-left:8px;color:#999;font-size:12px">数字越小优先级越高</span>
+        </el-form-item>
+        <el-form-item label="机器人名称">
+          <el-input v-model="form.robotName" placeholder="请输入机器人名称" />
+        </el-form-item>
+        <el-form-item label="启用机器人">
+          <el-switch v-model="form.robotEnabled" />
+        </el-form-item>
+        <el-form-item label="转人工阈值">
+          <el-input-number v-model="form.autoTransferThreshold" :min="1" :max="20" />
+          <span style="margin-left:8px;color:#999;font-size:12px">轮对话后自动转人工</span>
+        </el-form-item>
+        <el-form-item label="启用渠道">
+          <el-switch v-model="form.enabled" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveChannel">保存</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { ChatChannel } from '@/types/page';
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const isEdit = ref(false);
+const currentId = ref('');
+
+const channelColors: Record<string, string> = {
+  taobao: '#ff5000',
+  tmall: '#ff6f00',
+  jd: '#e1251b',
+  pdd: '#e2231a',
+  douyin: '#000000',
+  wechat: '#07c160',
+  website: '#4a90e2'
+};
+
+const channels = ref<ChatChannel[]>([
+  { id: '1', name: '淘宝', code: 'taobao', icon: '', enabled: true, robotName: '淘宝小秘', robotEnabled: true, autoTransferThreshold: 5, priority: 1 },
+  { id: '2', name: '天猫', code: 'tmall', icon: '', enabled: true, robotName: '天猫客服', robotEnabled: true, autoTransferThreshold: 5, priority: 2 },
+  { id: '3', name: '京东', code: 'jd', icon: '', enabled: true, robotName: '京东小哥', robotEnabled: true, autoTransferThreshold: 6, priority: 3 },
+  { id: '4', name: '拼多多', code: 'pdd', icon: '', enabled: true, robotName: '拼多多客服', robotEnabled: true, autoTransferThreshold: 4, priority: 4 },
+  { id: '5', name: '抖音', code: 'douyin', icon: '', enabled: true, robotName: '抖音小助手', robotEnabled: true, autoTransferThreshold: 5, priority: 5 },
+  { id: '6', name: '微信', code: 'wechat', icon: '', enabled: false, robotName: '微信客服', robotEnabled: false, autoTransferThreshold: 3, priority: 6 },
+  { id: '7', name: '独立站', code: 'website', icon: '', enabled: true, robotName: '网站客服', robotEnabled: true, autoTransferThreshold: 7, priority: 7 }
+]);
+
+const form = ref<Partial<ChatChannel>>({
+  priority: 5,
+  autoTransferThreshold: 5,
+  robotEnabled: true,
+  enabled: true
+});
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+
+const openCreate = () => {
+  isEdit.value = false;
+  form.value = { priority: 5, autoTransferThreshold: 5, robotEnabled: true, enabled: true };
+  dialogVisible.value = true;
+};
+
+const openEdit = (row: ChatChannel) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  form.value = { ...row };
+  dialogVisible.value = true;
+};
+
+const resetForm = () => {
+  form.value = { priority: 5, autoTransferThreshold: 5, robotEnabled: true, enabled: true };
+};
+
+const saveChannel = () => {
+  if (!form.value.name || !form.value.code) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  if (isEdit.value) {
+    const idx = channels.value.findIndex(c => c.id === currentId.value);
+    if (idx !== -1) channels.value[idx] = { ...channels.value[idx], ...form.value } as ChatChannel;
+    ElMessage.success('更新成功');
+  } else {
+    channels.value.push({
+      id: Date.now().toString(),
+      icon: '',
+      ...form.value
+    } as ChatChannel);
+    ElMessage.success('创建成功');
+  }
+  dialogVisible.value = false;
+};
+
+const toggleEnabled = (row: ChatChannel, val: boolean) => {
+  row.enabled = val;
+  ElMessage.success(`已${val ? '启用' : '禁用'}`);
+};
+
+const toggleRobot = (row: ChatChannel) => {
+  row.robotEnabled = !row.robotEnabled;
+  ElMessage.success(`机器人已${row.robotEnabled ? '启用' : '禁用'}`);
+};
+
+const handleDelete = (row: ChatChannel) => {
+  ElMessageBox.confirm('确定删除该渠道吗?', '提示', { type: 'warning' })
+    .then(() => {
+      channels.value = channels.value.filter(c => c.id !== row.id);
+      ElMessage.success('删除成功');
+    })
+    .catch(() => {});
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.channel-icon {
+  width: 28px;
+  height: 28px;
+  border-radius: 6px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  color: #fff;
+  font-weight: 700;
+  font-size: 14px;
+}
+.guide-content {
+  padding: 8px 0;
+}
+.guide-content ol {
+  margin: 8px 0;
+  padding-left: 20px;
+}
+.guide-content li {
+  margin: 4px 0;
+  color: #666;
+}
+.code-block {
+  background: #f5f5f5;
+  padding: 12px;
+  border-radius: 6px;
+  font-size: 12px;
+  overflow-x: auto;
+  margin-top: 8px;
+}
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #f0f0f0;
+}
+.section-header h3 { margin: 0; font-size: 15px; font-weight: 600; color: #333; }
+.channel-guide {
+  padding: 16px;
+}
+</style>

+ 365 - 0
src/views/crm/ChatLogView.vue

@@ -0,0 +1,365 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <el-form :model="filters" inline class="filter-form">
+        <el-form-item label="时间范围">
+          <el-date-picker v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:240px" />
+        </el-form-item>
+        <el-form-item label="渠道">
+          <el-select v-model="filters.channel" placeholder="全部渠道" clearable style="width:130px">
+            <el-option label="淘宝" value="淘宝" />
+            <el-option label="京东" value="京东" />
+            <el-option label="拼多多" value="拼多多" />
+            <el-option label="天猫" value="天猫" />
+            <el-option label="独立站" value="独立站" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="处理客服">
+          <el-select v-model="filters.agentId" placeholder="全部客服" clearable style="width:130px">
+            <el-option v-for="agent in agents" :key="agent.id" :label="agent.name" :value="agent.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="满意度">
+          <el-select v-model="filters.satisfaction" placeholder="全部" clearable style="width:120px">
+            <el-option label="⭐⭐⭐⭐⭐" value="5" />
+            <el-option label="⭐⭐⭐⭐" value="4" />
+            <el-option label="⭐⭐⭐" value="3" />
+            <el-option label="⭐⭐" value="2" />
+            <el-option label="⭐" value="1" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="loadData">查询</el-button>
+          <el-button @click="resetFilters">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </section>
+
+    <section class="glass-card section-card" style="padding:12px 24px">
+      <div class="table-toolbar" style="margin-bottom:0">
+        <div class="chip-list">
+          <el-button @click="batchExport">导出</el-button>
+          <el-button @click="batchMark">批量标记</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredLogs" stripe style="width:100%" v-loading="loading" @selection-change="handleSelection">
+        <el-table-column type="selection" width="50" />
+        <el-table-column prop="id" label="会话ID" width="120" />
+        <el-table-column prop="createdAt" label="会话时间" width="160">
+          <template #default="{ row }">
+            <div>{{ row.createdAt }}</div>
+            <div style="font-size:11px;color:#999">{{ row.lastMessageAt }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="visitorName" label="访客" width="120">
+          <template #default="{ row }">
+            <div style="display:flex;align-items:center;gap:6px">
+              <el-avatar :size="24">{{ row.visitorName[0] }}</el-avatar>
+              <span>{{ row.visitorName }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="channel" label="渠道" width="90" align="center">
+          <template #default="{ row }">
+            <el-tag size="small">{{ row.channel }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="shopName" label="店铺" width="100" align="center" />
+        <el-table-column prop="status" label="状态" width="90" align="center">
+          <template #default="{ row }">
+            <el-tag :type="statusType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="aiHandled" label="处理方式" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag v-if="row.aiHandled" type="success" size="small">AI接待</el-tag>
+            <el-tag v-else type="warning" size="small">人工客服</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="agentName" label="处理客服" width="100" align="center">
+          <template #default="{ row }">
+            {{ row.agentName || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="satisfaction" label="满意度" width="100" align="center">
+          <template #default="{ row }">
+            <template v-if="row.satisfaction">
+              <el-rate :model-value="row.satisfaction" disabled text-color="#ff9900" />
+            </template>
+            <span v-else style="color:#999">未评价</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="100" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="viewDetail(row)">查看</el-button>
+            <el-button link type="success" @click="markUseful(row)">标优</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="detailVisible" title="会话详情" width="800px">
+      <template v-if="currentLog">
+        <div class="chat-detail">
+          <div class="chat-detail__header">
+            <div class="chat-detail__info">
+              <el-avatar :size="48">{{ currentLog.visitorName[0] }}</el-avatar>
+              <div>
+                <div class="chat-detail__name">{{ currentLog.visitorName }}</div>
+                <div class="chat-detail__meta">
+                  <el-tag size="small">{{ currentLog.channel }}</el-tag>
+                  <span>{{ currentLog.shopName }}</span>
+                  <span>{{ currentLog.source }}</span>
+                  <span>{{ currentLog.device }}</span>
+                </div>
+              </div>
+            </div>
+            <div class="chat-detail__stats">
+              <div class="chat-detail__stat">
+                <span class="chat-detail__stat-label">会话时长</span>
+                <span class="chat-detail__stat-value">5分32秒</span>
+              </div>
+              <div class="chat-detail__stat">
+                <span class="chat-detail__stat-label">消息数</span>
+                <span class="chat-detail__stat-value">{{ currentLog.messages.length }}</span>
+              </div>
+              <div class="chat-detail__stat">
+                <span class="chat-detail__stat-label">满意度</span>
+                <el-rate v-if="currentLog.satisfaction" :model-value="currentLog.satisfaction" disabled size="small" />
+                <span v-else>未评价</span>
+              </div>
+            </div>
+          </div>
+
+          <div class="chat-detail__messages">
+            <div 
+              v-for="msg in currentLog.messages" 
+              :key="msg.id"
+              class="chat-message"
+              :class="`chat-message--${msg.role}`"
+            >
+              <div class="chat-message__avatar">
+                {{ msg.role === 'visitor' ? currentLog.visitorName[0] : msg.role === 'ai' ? '🤖' : '👤' }}
+              </div>
+              <div class="chat-message__content">
+                <div class="chat-message__bubble">{{ msg.content }}</div>
+                <div class="chat-message__time">{{ msg.timestamp }}</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import type { ChatSession, ChatMessage } from '@/types/page';
+
+const loading = ref(false);
+const detailVisible = ref(false);
+const currentLog = ref<ChatSession | null>(null);
+const selectedIds = ref<string[]>([]);
+
+const filters = ref({ dateRange: [], channel: '', agentId: '', satisfaction: '' });
+
+const agents = [
+  { id: 'A001', name: '张三' },
+  { id: 'A002', name: '李四' },
+  { id: 'A003', name: '王五' },
+  { id: 'A004', name: '赵六' }
+];
+
+const logs = ref<ChatSession[]>([
+  { 
+    id: 'CS20240115001', visitorId: 'V001', visitorName: '张小姐', visitorAvatar: '', channel: '淘宝', shopName: '旗舰店', messages: [
+      { id: '1', role: 'visitor', content: '你好,我想问一下我的订单什么时候能到?', timestamp: '14:23:15' },
+      { id: '2', role: 'ai', content: '您好!我是智能客服小C,请问您的订单号是多少?', timestamp: '14:23:18' },
+      { id: '3', role: 'visitor', content: '订单号是 DD20240115001', timestamp: '14:24:02' },
+      { id: '4', role: 'ai', content: '查询到您的订单已于1月15日发货,预计1月18日-20日送达。', timestamp: '14:24:08' }
+    ], aiHandled: true, agentId: '', agentName: '', satisfaction: 5, status: 'ended', source: '淘宝APP', device: 'iPhone 14', location: '广东广州', createdAt: '2024-01-15 14:23', endedAt: '2024-01-15 14:28', lastMessageAt: '14:24:08' },
+  { 
+    id: 'CS20240115002', visitorId: 'V002', visitorName: '李先生', visitorAvatar: '', channel: '京东', shopName: '自营店', messages: [
+      { id: '1', role: 'visitor', content: '这个商品有货吗?', timestamp: '15:30:10' },
+      { id: '2', role: 'agent', content: '您好,这款商品还有库存,请问需要什么颜色和尺码?', timestamp: '15:30:25' },
+      { id: '3', role: 'visitor', content: '黑色的M码', timestamp: '15:31:02' },
+      { id: '4', role: 'agent', content: '好的,黑色M码有货,我帮您下单。', timestamp: '15:31:15' }
+    ], aiHandled: false, agentId: 'A001', agentName: '张三', satisfaction: 5, status: 'ended', source: '京东APP', device: '小米13', location: '北京朝阳', createdAt: '2024-01-15 15:30', endedAt: '2024-01-15 15:35', lastMessageAt: '15:31:15' },
+  { 
+    id: 'CS20240115003', visitorId: 'V003', visitorName: '王女士', visitorAvatar: '', channel: '拼多多', shopName: '官方店', messages: [
+      { id: '1', role: 'visitor', content: '申请退货', timestamp: '16:45:30' },
+      { id: '2', role: 'ai', content: '请问是什么原因要退货呢?', timestamp: '16:45:35' },
+      { id: '3', role: 'visitor', content: '尺寸不合适,偏大了', timestamp: '16:46:10' },
+      { id: '4', role: 'ai', content: '了解了,您可以登录账号申请退换货,我们支持7天无理由退货。', timestamp: '16:46:20' }
+    ], aiHandled: true, agentId: '', agentName: '', satisfaction: 4, status: 'ended', source: '拼多多APP', device: 'OPPO Reno9', location: '上海浦东', createdAt: '2024-01-15 16:45', endedAt: '2024-01-15 16:50', lastMessageAt: '16:46:20' },
+  { 
+    id: 'CS20240115004', visitorId: 'V004', visitorName: '赵先生', visitorAvatar: '', channel: '天猫', shopName: '旗舰店', messages: [
+      { id: '1', role: 'visitor', content: '你好', timestamp: '17:20:00' },
+      { id: '2', role: 'ai', content: '您好!很高兴为您服务。', timestamp: '17:20:03' },
+      { id: '3', role: 'visitor', content: '谢谢', timestamp: '17:20:30' },
+      { id: '4', role: 'ai', content: '不客气!祝您购物愉快!', timestamp: '17:20:35' }
+    ], aiHandled: true, agentId: '', agentName: '', satisfaction: 5, status: 'ended', source: '天猫APP', device: '华为Mate60', location: '深圳南山', createdAt: '2024-01-15 17:20', endedAt: '2024-01-15 17:21', lastMessageAt: '17:20:35' },
+  { 
+    id: 'CS20240115005', visitorId: 'V005', visitorName: '陈小姐', visitorAvatar: '', channel: '独立站', shopName: '官网', messages: [
+      { id: '1', role: 'visitor', content: 'Do you ship internationally?', timestamp: '18:00:00' },
+      { id: '2', role: 'agent', content: 'Yes, we ship to most countries. International shipping takes 7-15 business days.', timestamp: '18:00:30' }
+    ], aiHandled: false, agentId: 'A002', agentName: '李四', satisfaction: undefined, status: 'ended', source: 'Google搜索', device: 'Chrome/Win10', location: '美国加州', createdAt: '2024-01-15 18:00', endedAt: '2024-01-15 18:02', lastMessageAt: '18:00:30' }
+]);
+
+const filteredLogs = computed(() => {
+  return logs.value;
+});
+
+const statusLabel = (status: string) => {
+  const map: Record<string, string> = { waiting: '等待中', ongoing: '进行中', ended: '已结束' };
+  return map[status] || status;
+};
+
+const statusType = (status: string) => {
+  const map: Record<string, string> = { waiting: 'warning', ongoing: 'primary', ended: 'info' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { dateRange: [], channel: '', agentId: '', satisfaction: '' }; };
+
+const handleSelection = (rows: ChatSession[]) => {
+  selectedIds.value = rows.map(r => r.id);
+};
+
+const viewDetail = (row: ChatSession) => {
+  currentLog.value = row;
+  detailVisible.value = true;
+};
+
+const markUseful = (row: ChatSession) => {
+  ElMessage.success('已标记为优质对话');
+};
+
+const batchExport = () => { ElMessage.info('导出开始'); };
+const batchMark = () => { 
+  if (selectedIds.value.length === 0) {
+    ElMessage.warning('请先选择要标记的会话');
+    return;
+  }
+  ElMessage.success(`已标记 ${selectedIds.value.length} 条会话`);
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+.chat-detail__header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  padding-bottom: 16px;
+  border-bottom: 1px solid #eee;
+  margin-bottom: 16px;
+}
+.chat-detail__info {
+  display: flex;
+  gap: 12px;
+}
+.chat-detail__name {
+  font-size: 16px;
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+.chat-detail__meta {
+  display: flex;
+  gap: 8px;
+  font-size: 13px;
+  color: #666;
+}
+.chat-detail__stats {
+  display: flex;
+  gap: 24px;
+}
+.chat-detail__stat {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+}
+.chat-detail__stat-label {
+  font-size: 12px;
+  color: #999;
+}
+.chat-detail__stat-value {
+  font-size: 16px;
+  font-weight: 600;
+}
+.chat-detail__messages {
+  max-height: 400px;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+.chat-message {
+  display: flex;
+  gap: 10px;
+  max-width: 80%;
+}
+.chat-message--visitor {
+  align-self: flex-end;
+  flex-direction: row-reverse;
+}
+.chat-message--ai,
+.chat-message--agent {
+  align-self: flex-start;
+}
+.chat-message__avatar {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  background: #f0f0f0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+  flex-shrink: 0;
+}
+.chat-message__content {
+  display: flex;
+  flex-direction: column;
+}
+.chat-message--visitor .chat-message__content {
+  align-items: flex-end;
+}
+.chat-message__bubble {
+  padding: 10px 14px;
+  border-radius: 16px;
+  font-size: 14px;
+  line-height: 1.5;
+}
+.chat-message--visitor .chat-message__bubble {
+  background: var(--cb-primary);
+  color: #fff;
+  border-bottom-right-radius: 4px;
+}
+.chat-message--ai .chat-message__bubble {
+  background: #fff;
+  color: #333;
+  border-bottom-left-radius: 4px;
+  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
+}
+.chat-message--agent .chat-message__bubble {
+  background: #e8f4ff;
+  color: #333;
+  border-bottom-left-radius: 4px;
+}
+.chat-message__time {
+  font-size: 11px;
+  color: #999;
+  margin-top: 4px;
+}
+</style>

+ 320 - 0
src/views/crm/KnowledgeBaseView.vue

@@ -0,0 +1,320 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <el-form :model="filters" inline class="filter-form">
+        <el-form-item label="分类">
+          <el-select v-model="filters.category" placeholder="全部分类" clearable style="width:150px">
+            <el-option v-for="cat in categories" :key="cat.id" :label="cat.name" :value="cat.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="关键词">
+          <el-input v-model="filters.keyword" placeholder="搜索关键词" clearable style="width:160px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-select v-model="filters.status" placeholder="全部" clearable style="width:120px">
+            <el-option label="已启用" value="enabled" />
+            <el-option label="已禁用" value="disabled" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="loadData">查询</el-button>
+          <el-button @click="resetFilters">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </section>
+
+    <section class="glass-card section-card" style="padding:12px 24px">
+      <div class="table-toolbar" style="margin-bottom:0">
+        <div class="chip-list">
+          <el-button type="primary" @click="openCreateCategory">新建分类</el-button>
+          <el-button type="success" @click="openCreate">新增知识</el-button>
+          <el-button @click="batchImport">批量导入</el-button>
+          <el-button @click="doExport">导出</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="knowledge-layout">
+        <div class="category-tree">
+          <div class="category-tree__header">
+            <h3>知识分类</h3>
+          </div>
+          <el-tree 
+            :data="categoryTree" 
+            :props="{ children: 'children', label: 'name' }"
+            node-key="id"
+            @node-click="handleCategoryClick"
+            default-expand-all
+          >
+            <template #default="{ node, data }">
+              <span class="category-node">
+                <span>{{ data.name }}</span>
+                <span class="category-node__count">({{ data.count }})</span>
+              </span>
+            </template>
+          </el-tree>
+        </div>
+
+        <div class="knowledge-list">
+          <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+            <el-table-column prop="keywords" label="关键词" width="200">
+              <template #default="{ row }">
+                <el-tag v-for="kw in row.keywords.slice(0, 3)" :key="kw" size="small" style="margin-right:4px">{{ kw }}</el-tag>
+                <span v-if="row.keywords.length > 3" style="color:#999;font-size:12px">+{{ row.keywords.length - 3 }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="question" label="标准问题" min-width="200" show-overflow-tooltip />
+            <el-table-column prop="answer" label="标准答案" min-width="250" show-overflow-tooltip />
+            <el-table-column prop="clicks" label="点击量" width="90" align="center" />
+            <el-table-column prop="aiScore" label="AI匹配度" width="100" align="center">
+              <template #default="{ row }">
+                <el-progress :percentage="row.aiScore" :status="aiScoreStatus(row.aiScore)" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="status" label="状态" width="90" align="center">
+              <template #default="{ row }">
+                <el-tag :type="row.status === 'enabled' ? 'success' : 'info'" size="small">
+                  {{ row.status === 'enabled' ? '启用' : '禁用' }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="updatedAt" label="更新时间" width="120" />
+            <el-table-column label="操作" width="150" fixed="right">
+              <template #default="{ row }">
+                <el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+                <el-button link :type="row.status === 'enabled' ? 'warning' : 'success'" @click="toggleStatus(row)">
+                  {{ row.status === 'enabled' ? '禁用' : '启用' }}
+                </el-button>
+                <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </div>
+    </section>
+
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px" @closed="resetForm">
+      <el-form :model="form" label-width="100px">
+        <el-form-item label="所属分类" required>
+          <el-cascader v-model="form.categoryId" :options="categoryOptions" :props="{ checkStrictly: true, label: 'name', value: 'id' }" placeholder="选择分类" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="关键词" required>
+          <el-select v-model="form.keywords" multiple filterable allow-create default-first-option placeholder="输入关键词后回车" style="width:100%">
+            <el-option v-for="kw in form.keywords" :key="kw" :label="kw" :value="kw" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="标准问题" required>
+          <el-input v-model="form.question" type="textarea" :rows="2" placeholder="请输入标准问题" />
+        </el-form-item>
+        <el-form-item label="标准答案" required>
+          <el-input v-model="form.answer" type="textarea" :rows="4" placeholder="请输入标准答案" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio label="enabled">启用</el-radio>
+            <el-radio label="disabled">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveKnowledge">保存</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="categoryDialogVisible" title="新建分类" width="400px">
+      <el-form :model="categoryForm" label-width="80px">
+        <el-form-item label="上级分类">
+          <el-tree-select v-model="categoryForm.parentId" :data="categoryTree" :props="{ label: 'name', value: 'id' }" placeholder="选择上级分类(可选)" clearable check-strictly style="width:100%" />
+        </el-form-item>
+        <el-form-item label="分类名称" required>
+          <el-input v-model="categoryForm.name" placeholder="请输入分类名称" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="categoryDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveCategory">保存</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { KnowledgeBaseItem, KnowledgeCategory } from '@/types/page';
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const categoryDialogVisible = ref(false);
+const isEdit = ref(false);
+const currentId = ref('');
+
+const filters = ref({ category: '', keyword: '', status: '' });
+
+const categories = ref<KnowledgeCategory[]>([
+  { id: '1', name: '产品咨询', count: 45 },
+  { id: '2', name: '物流问题', count: 32 },
+  { id: '3', name: '支付问题', count: 18 },
+  { id: '4', name: '退换货', count: 28 },
+  { id: '5', name: '账户问题', count: 15 }
+]);
+
+const categoryTree = ref<KnowledgeCategory[]>([
+  { id: '1', name: '产品咨询', count: 45, children: [
+    { id: '1-1', name: '产品规格', count: 20 },
+    { id: '1-2', name: '产品材质', count: 15 },
+    { id: '1-3', name: '使用方法', count: 10 }
+  ]},
+  { id: '2', name: '物流问题', count: 32, children: [
+    { id: '2-1', name: '发货时间', count: 12 },
+    { id: '2-2', name: '物流查询', count: 10 },
+    { id: '2-3', name: '快递丢失', count: 10 }
+  ]},
+  { id: '3', name: '支付问题', count: 18 },
+  { id: '4', name: '退换货', count: 28 },
+  { id: '5', name: '账户问题', count: 15 }
+]);
+
+const categoryOptions = computed(() => categoryTree.value);
+
+const items = ref<KnowledgeBaseItem[]>([
+  { id: 'K001', category: '产品咨询', categoryId: '1', keywords: ['物流', '快递', '发货', '到货'], question: '你们的物流一般多久到?', answer: '国内普通地区一般3-5天送达,偏远地区5-7天。海外仓根据地区不同,一般7-15天。如有特殊情况,我们会提前通知您。', clicks: 1234, aiScore: 95, status: 'enabled', createdAt: '2024-01-10', updatedAt: '2024-01-15' },
+  { id: 'K002', category: '产品咨询', categoryId: '1', keywords: ['材质', '面料', '材料'], question: '这款产品是什么材质的?', answer: '本产品采用优质牛皮材质,外层覆膜保护,内里透气舒适。具体材质成分请参考详情页描述。', clicks: 856, aiScore: 88, status: 'enabled', createdAt: '2024-01-08', updatedAt: '2024-01-14' },
+  { id: 'K003', category: '物流问题', categoryId: '2', keywords: ['查询', '单号', '追踪'], question: '如何查询我的订单物流?', answer: '您可以登录账号,在"我的订单"中找到对应订单,点击"查看物流"即可追踪。如有疑问可联系客服。', clicks: 2341, aiScore: 92, status: 'enabled', createdAt: '2024-01-05', updatedAt: '2024-01-12' },
+  { id: 'K004', category: '支付问题', categoryId: '3', keywords: ['支付', '退款', '到账'], question: '退款多久能到账?', answer: '退款将在收到退货并确认无误后1-7个工作日内原路返回。信用卡退款可能需要3-5个工作日。', clicks: 567, aiScore: 85, status: 'enabled', createdAt: '2024-01-03', updatedAt: '2024-01-10' },
+  { id: 'K005', category: '退换货', categoryId: '4', keywords: ['退货', '换货', '售后'], question: '支持七天无理由退货吗?', answer: '本店支持7天无理由退货(定制商品除外)。请确保商品未使用、包装完整。退货请联系客服申请。', clicks: 1892, aiScore: 97, status: 'enabled', createdAt: '2024-01-01', updatedAt: '2024-01-08' },
+  { id: 'K006', category: '产品咨询', categoryId: '1', keywords: ['尺寸', '大小', '规格'], question: '如何选择合适的尺寸?', answer: '请参考详情页的尺码表,根据您的身高体重选择。如仍有疑问,可咨询客服提供专业建议。', clicks: 743, aiScore: 90, status: 'enabled', createdAt: '2024-01-02', updatedAt: '2024-01-09' },
+  { id: 'K007', category: '物流问题', categoryId: '2', keywords: ['延迟', '延误', '超时'], question: '订单延迟了怎么办?', answer: '如遇物流延迟,请先查询快递公司官网了解情况。如长时间未更新,请联系客服协助查询或申请赔偿。', clicks: 421, aiScore: 82, status: 'disabled', createdAt: '2023-12-28', updatedAt: '2024-01-05' }
+]);
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.category && item.categoryId !== filters.value.category) return false;
+    if (filters.value.keyword && !item.keywords.some(k => k.includes(filters.value.keyword)) && !item.question.includes(filters.value.keyword)) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const form = ref<Partial<KnowledgeBaseItem>>({
+  categoryId: '',
+  keywords: [],
+  question: '',
+  answer: '',
+  status: 'enabled'
+});
+
+const categoryForm = ref({ parentId: '', name: '' });
+
+const dialogTitle = computed(() => isEdit.value ? '编辑知识' : '新增知识');
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { category: '', keyword: '', status: '' }; };
+
+const handleCategoryClick = (data: KnowledgeCategory) => {
+  filters.value.category = data.id;
+};
+
+const aiScoreStatus = (score: number) => {
+  if (score >= 90) return 'success';
+  if (score >= 70) return 'warning';
+  return 'exception';
+};
+
+const openCreate = () => { isEdit.value = false; dialogVisible.value = true; };
+const openEdit = (row: KnowledgeBaseItem) => { isEdit.value = true; currentId.value = row.id; form.value = { ...row }; dialogVisible.value = true; };
+
+const openCreateCategory = () => { categoryForm.value = { parentId: '', name: '' }; categoryDialogVisible.value = true; };
+
+const saveKnowledge = () => {
+  if (!form.value.categoryId || !form.value.question || !form.value.answer) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  if (isEdit.value) {
+    const idx = items.value.findIndex(i => i.id === currentId.value);
+    if (idx !== -1) items.value[idx] = { ...items.value[idx], ...form.value } as KnowledgeBaseItem;
+    ElMessage.success('更新成功');
+  } else {
+    items.value.unshift({
+      id: 'K' + Date.now(),
+      category: categories.value.find(c => c.id === form.value.categoryId)?.name || '',
+      ...form.value,
+      clicks: 0,
+      aiScore: 0,
+      createdAt: new Date().toISOString().split('T')[0],
+      updatedAt: new Date().toISOString().split('T')[0]
+    } as KnowledgeBaseItem);
+    ElMessage.success('创建成功');
+  }
+  dialogVisible.value = false;
+};
+
+const resetForm = () => { form.value = { categoryId: '', keywords: [], question: '', answer: '', status: 'enabled' }; };
+
+const saveCategory = () => {
+  if (!categoryForm.value.name) {
+    ElMessage.warning('请输入分类名称');
+    return;
+  }
+  ElMessage.success('分类创建成功');
+  categoryDialogVisible.value = false;
+};
+
+const toggleStatus = (row: KnowledgeBaseItem) => {
+  row.status = row.status === 'enabled' ? 'disabled' : 'enabled';
+  ElMessage.success(`已${row.status === 'enabled' ? '启用' : '禁用'}`);
+};
+
+const handleDelete = (row: KnowledgeBaseItem) => {
+  ElMessageBox.confirm('确定删除该知识吗?', '提示', { type: 'warning' })
+    .then(() => {
+      items.value = items.value.filter(i => i.id !== row.id);
+      ElMessage.success('删除成功');
+    })
+    .catch(() => {});
+};
+
+const batchImport = () => { ElMessage.info('批量导入功能开发中'); };
+const doExport = () => { ElMessage.info('导出开始'); };
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+.knowledge-layout {
+  display: grid;
+  grid-template-columns: 240px 1fr;
+  gap: 16px;
+  min-height: 500px;
+}
+.category-tree {
+  background: #fafafa;
+  border-radius: 12px;
+  padding: 16px;
+}
+.category-tree__header {
+  margin-bottom: 16px;
+}
+.category-tree__header h3 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+.category-node {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+.category-node__count {
+  color: #999;
+  font-size: 12px;
+}
+.knowledge-list {
+  min-width: 0;
+}
+</style>

+ 301 - 0
src/views/crm/ServicePerformanceView.vue

@@ -0,0 +1,301 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <el-form :model="filters" inline class="filter-form">
+        <el-form-item label="时间">
+          <el-date-picker v-model="filters.date" type="month" placeholder="选择月份" style="width:130px" />
+        </el-form-item>
+        <el-form-item label="部门">
+          <el-select v-model="filters.department" placeholder="全部部门" clearable style="width:140px">
+            <el-option label="客服一部" value="一部" />
+            <el-option label="客服二部" value="二部" />
+            <el-option label="VIP客服组" value="VIP组" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="loadData">查询</el-button>
+          <el-button @click="resetFilters">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </section>
+
+    <section class="glass-card section-card" style="padding:16px">
+      <div class="stat-grid" style="grid-template-columns:repeat(4, 1fr)">
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">总接待量</div>
+            <div class="stat-card__value" style="font-size:28px">{{ summaryStats.totalHandle }}</div>
+            <div class="stat-card__trend up"><el-icon><ArrowUp /></el-icon>12%</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">平均响应时长</div>
+            <div class="stat-card__value" style="font-size:28px">{{ summaryStats.avgResponseTime }}</div>
+            <div class="stat-card__trend down"><el-icon><ArrowDown /></el-icon>8%</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">平均满意度</div>
+            <div class="stat-card__value" style="font-size:28px">{{ summaryStats.avgSatisfaction }}%</div>
+            <div class="stat-card__trend up"><el-icon><ArrowUp /></el-icon>2%</div>
+          </div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">AI辅助率</div>
+            <div class="stat-card__value" style="font-size:28px">{{ summaryStats.aiAssistRate }}%</div>
+            <div class="stat-card__trend up"><el-icon><ArrowUp /></el-icon>5%</div>
+          </div>
+        </article>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="performance-layout">
+        <div class="performance-ranking">
+          <div class="section-header">
+            <h3>绩效排名</h3>
+            <el-radio-group v-model="rankType" size="small">
+              <el-radio-button label="handle">接待量</el-radio-button>
+              <el-radio-button label="satisfaction">满意度</el-radio-button>
+              <el-radio-button label="response">响应速度</el-radio-button>
+            </el-radio-group>
+          </div>
+          <div class="ranking-list">
+            <div 
+              v-for="(agent, index) in rankedAgents" 
+              :key="agent.id"
+              class="ranking-item"
+              :class="{ 'ranking-item--top3': index < 3 }"
+            >
+              <div class="ranking-item__rank">
+                <el-badge v-if="index < 3" :value="index + 1" :type="index === 0 ? 'gold' : index === 1 ? 'silver' : 'bronze'" />
+                <span v-else class="rank-num">{{ index + 1 }}</span>
+              </div>
+              <el-avatar :size="40" :src="agent.agentAvatar">{{ agent.agentName[0] }}</el-avatar>
+              <div class="ranking-item__info">
+                <div class="ranking-item__name">{{ agent.agentName }}</div>
+                <div class="ranking-item__dept">{{ agent.department }}</div>
+              </div>
+              <div class="ranking-item__score">
+                <div class="ranking-item__main">{{ getRankScore(agent, rankType) }}</div>
+                <el-rate v-if="rankType === 'satisfaction'" :model-value="agent.satisfaction" disabled text-color="#ff9900" score-template="{value}" />
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="performance-table">
+          <div class="section-header">
+            <h3>详细数据</h3>
+            <el-button size="small" @click="doExport">导出报表</el-button>
+          </div>
+          <el-table :data="performanceData" stripe style="width:100%" v-loading="loading">
+            <el-table-column prop="agentName" label="客服" width="120">
+              <template #default="{ row }">
+                <div style="display:flex;align-items:center;gap:8px">
+                  <el-avatar :size="32">{{ row.agentName[0] }}</el-avatar>
+                  <span>{{ row.agentName }}</span>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column prop="department" label="部门" width="100" align="center" />
+            <el-table-column prop="handleCount" label="接待量" width="90" align="center" sortable />
+            <el-table-column prop="aiAssistCount" label="AI辅助" width="90" align="center">
+              <template #default="{ row }">
+                <span style="color:var(--cb-primary)">{{ row.aiAssistCount }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="avgResponseTime" label="平均响应" width="100" align="center" sortable>
+              <template #default="{ row }">
+                <span :class="responseTimeClass(row.avgResponseTime)">{{ row.avgResponseTime }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="avgFirstResponseTime" label="首次响应" width="100" align="center" />
+            <el-table-column prop="satisfaction" label="满意度" width="120" align="center" sortable>
+              <template #default="{ row }">
+                <el-progress :percentage="row.satisfaction" :status="satisfactionStatus(row.satisfaction)" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="solveRate" label="解决率" width="100" align="center">
+              <template #default="{ row }">
+                <el-progress :percentage="row.solveRate" :status="solveRateStatus(row.solveRate)" />
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </div>
+    </section>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import type { ServicePerformance } from '@/types/page';
+
+const loading = ref(false);
+const rankType = ref('handle');
+const filters = ref({ date: '', department: '' });
+
+const summaryStats = ref({
+  totalHandle: 1234,
+  avgResponseTime: '32秒',
+  avgSatisfaction: 96.5,
+  aiAssistRate: 78.2
+});
+
+const performanceData = ref<ServicePerformance[]>([
+  { id: '1', agentId: 'A001', agentName: '张三', agentAvatar: '', department: '客服一部', handleCount: 234, aiAssistCount: 189, avgResponseTime: '28秒', avgFirstResponseTime: '5秒', satisfaction: 98, solveRate: 95, date: '2024-01' },
+  { id: '2', agentId: 'A002', agentName: '李四', agentAvatar: '', department: '客服一部', handleCount: 198, aiAssistCount: 156, avgResponseTime: '32秒', avgFirstResponseTime: '8秒', satisfaction: 97, solveRate: 93, date: '2024-01' },
+  { id: '3', agentId: 'A003', agentName: '王五', agentAvatar: '', department: '客服二部', handleCount: 176, aiAssistCount: 142, avgResponseTime: '35秒', avgFirstResponseTime: '10秒', satisfaction: 95, solveRate: 91, date: '2024-01' },
+  { id: '4', agentId: 'A004', agentName: '赵六', agentAvatar: '', department: '客服二部', handleCount: 165, aiAssistCount: 128, avgResponseTime: '38秒', avgFirstResponseTime: '12秒', satisfaction: 94, solveRate: 89, date: '2024-01' },
+  { id: '5', agentId: 'A005', agentName: '钱七', agentAvatar: '', department: 'VIP客服组', handleCount: 156, aiAssistCount: 98, avgResponseTime: '25秒', avgFirstResponseTime: '4秒', satisfaction: 99, solveRate: 98, date: '2024-01' },
+  { id: '6', agentId: 'A006', agentName: '孙八', agentAvatar: '', department: '客服一部', handleCount: 145, aiAssistCount: 112, avgResponseTime: '42秒', avgFirstResponseTime: '15秒', satisfaction: 92, solveRate: 87, date: '2024-01' },
+  { id: '7', agentId: 'A007', agentName: '周九', agentAvatar: '', department: '客服二部', handleCount: 134, aiAssistCount: 102, avgResponseTime: '45秒', avgFirstResponseTime: '18秒', satisfaction: 90, solveRate: 85, date: '2024-01' },
+  { id: '8', agentId: 'A008', agentName: '吴十', agentAvatar: '', department: 'VIP客服组', handleCount: 126, aiAssistCount: 78, avgResponseTime: '30秒', avgFirstResponseTime: '6秒', satisfaction: 96, solveRate: 94, date: '2024-01' }
+]);
+
+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);
+  });
+});
+
+const getRankScore = (agent: ServicePerformance, type: string) => {
+  if (type === 'handle') return agent.handleCount + ' 单';
+  if (type === 'satisfaction') return agent.satisfaction + '%';
+  return agent.avgResponseTime;
+};
+
+const responseTimeClass = (time: string) => {
+  const seconds = parseInt(time);
+  if (seconds <= 30) return 'text-success';
+  if (seconds <= 45) return 'text-warning';
+  return 'text-danger';
+};
+
+const satisfactionStatus = (val: number) => {
+  if (val >= 95) return 'success';
+  if (val >= 85) return 'warning';
+  return 'exception';
+};
+
+const solveRateStatus = (val: number) => {
+  if (val >= 90) return 'success';
+  if (val >= 80) return 'warning';
+  return 'exception';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { date: '', department: '' }; };
+const doExport = () => { ElMessage.info('导出开始'); };
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #f0f0f0;
+}
+.section-header h3 { margin: 0; font-size: 15px; font-weight: 600; color: #333; }
+.performance-layout {
+  display: grid;
+  grid-template-columns: 320px 1fr;
+  gap: 16px;
+}
+.performance-ranking {
+  background: #fafafa;
+  border-radius: 12px;
+  overflow: hidden;
+}
+.ranking-list {
+  padding: 12px;
+  max-height: 500px;
+  overflow-y: auto;
+}
+.ranking-item {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 12px;
+  background: #fff;
+  border-radius: 10px;
+  margin-bottom: 8px;
+  transition: all 0.2s;
+}
+.ranking-item:hover {
+  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
+}
+.ranking-item--top3 {
+  border-left: 3px solid var(--cb-primary);
+}
+.ranking-item__rank {
+  width: 32px;
+  display: flex;
+  justify-content: center;
+}
+.rank-num {
+  font-size: 16px;
+  font-weight: 600;
+  color: #999;
+}
+.ranking-item__info {
+  flex: 1;
+  min-width: 0;
+}
+.ranking-item__name {
+  font-weight: 600;
+  font-size: 14px;
+}
+.ranking-item__dept {
+  font-size: 12px;
+  color: #999;
+}
+.ranking-item__score {
+  text-align: right;
+}
+.ranking-item__main {
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--cb-primary);
+}
+.performance-table {
+  background: #fafafa;
+  border-radius: 12px;
+  overflow: hidden;
+}
+.text-success { color: #67c23a; font-weight: 600; }
+.text-warning { color: #e6a23c; font-weight: 600; }
+.text-danger { color: #f56c6c; font-weight: 600; }
+.stat-card { display: flex; align-items: center; gap: 12px; }
+.stat-card__icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
+.stat-card__icon svg { width: 24px; height: 24px; }
+.stat-card__content { flex: 1; min-width: 0; }
+.stat-card__label { font-size: 12px; color: #666; margin-bottom: 4px; }
+.stat-card__value { font-size: 24px; font-weight: 700; line-height: 1.2; }
+.stat-card__trend { font-size: 12px; margin-top: 4px; display: flex; align-items: center; gap: 2px; }
+.stat-card__trend.up { color: #11998e; }
+.stat-card__trend.down { color: #f5576c; }
+</style>

+ 17 - 7
src/views/finance/PaymentView.vue

@@ -126,6 +126,8 @@
 <script setup lang="ts">
 import { computed, onMounted, ref } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { api } from '@/api/services';
+import type { OrderItem } from '@/types/page';
 
 interface PaymentItem {
   id: string;
@@ -141,15 +143,16 @@ interface PaymentItem {
   transactionNo: string;
   fee: string;
   remark: string;
+  orderId?: string;
 }
 
 const items = ref<PaymentItem[]>([
-  { id: 'P001', paymentNo: 'PAY-20260420-001', channelOrderNo: 'SHOP-US-20260420-0123', channel: 'Shopify', shopName: 'NomadPeak US', currency: 'USD', amount: '128.00', payMethod: 'Credit Card', payTime: '2026-04-20 10:30:15', reconcileStatus: '待对账', transactionNo: 'txn_3N4K8M9L2', fee: '3.84', remark: '' },
-  { id: 'P002', paymentNo: 'PAY-20260420-002', channelOrderNo: 'SHOP-JP-20260420-0456', channel: 'Shopify', shopName: 'UrbanTrail JP', currency: 'JPY', amount: '8900', payMethod: 'PayPay', payTime: '2026-04-20 09:45:22', reconcileStatus: '已确认', transactionNo: 'txn_4P5Q9R8S3', fee: '267', remark: '' },
-  { id: 'P003', paymentNo: 'PAY-20260419-003', channelOrderNo: 'TT-UK-20260419-0789', channel: 'TikTok Shop', shopName: 'AeroDry UK', currency: 'GBP', amount: '65.00', payMethod: 'TikTok Pay', payTime: '2026-04-19 21:09:33', reconcileStatus: '已确认', transactionNo: 'txn_5T6U7V9W4', fee: '1.95', remark: '' },
-  { id: 'P004', paymentNo: 'PAY-20260419-004', channelOrderNo: 'SHOP-US-20260419-0321', channel: 'Shopify', shopName: 'NomadPeak US', currency: 'USD', amount: '259.00', payMethod: 'PayPal', payTime: '2026-04-19 18:20:45', reconcileStatus: '已确认', transactionNo: 'txn_6X7Y8Z9A5', fee: '7.77', remark: '' },
-  { id: 'P005', paymentNo: 'PAY-20260419-005', channelOrderNo: 'TT-UK-20260419-0654', channel: 'TikTok Shop', shopName: 'AeroDry UK', currency: 'GBP', amount: '156.00', payMethod: 'Credit Card', payTime: '2026-04-19 16:35:08', reconcileStatus: '有差异', transactionNo: 'txn_7B8C9D0E6', fee: '4.68', remark: '金额差异 $2.00' },
-  { id: 'P006', paymentNo: 'PAY-20260418-006', channelOrderNo: 'SHOP-JP-20260418-0987', channel: 'Shopify', shopName: 'UrbanTrail JP', currency: 'JPY', amount: '12500', payMethod: 'Credit Card', payTime: '2026-04-18 14:12:30', reconcileStatus: '已确认', transactionNo: 'txn_8F9G0H1I7', fee: '375', remark: '' }
+  { id: 'P001', paymentNo: 'PAY-20260420-001', channelOrderNo: 'SHOP-US-20260420-0123', channel: 'Shopify', shopName: 'NomadPeak US', currency: 'USD', amount: '128.00', payMethod: 'Credit Card', payTime: '2026-04-20 10:30:15', reconcileStatus: '待对账', transactionNo: 'txn_3N4K8M9L2', fee: '3.84', remark: '', orderId: 'order-1' },
+  { id: 'P002', paymentNo: 'PAY-20260420-002', channelOrderNo: 'SHOP-JP-20260420-0456', channel: 'Shopify', shopName: 'UrbanTrail JP', currency: 'JPY', amount: '8900', payMethod: 'PayPay', payTime: '2026-04-20 09:45:22', reconcileStatus: '已确认', transactionNo: 'txn_4P5Q9R8S3', fee: '267', remark: '', orderId: 'order-2' },
+  { id: 'P003', paymentNo: 'PAY-20260419-003', channelOrderNo: 'TT-UK-20260419-0789', channel: 'TikTok Shop', shopName: 'AeroDry UK', currency: 'GBP', amount: '65.00', payMethod: 'TikTok Pay', payTime: '2026-04-19 21:09:33', reconcileStatus: '已确认', transactionNo: 'txn_5T6U7V9W4', fee: '1.95', remark: '', orderId: 'order-3' },
+  { id: 'P004', paymentNo: 'PAY-20260419-004', channelOrderNo: 'SHOP-US-20260419-0321', channel: 'Shopify', shopName: 'NomadPeak US', currency: 'USD', amount: '259.00', payMethod: 'PayPal', payTime: '2026-04-19 18:20:45', reconcileStatus: '已确认', transactionNo: 'txn_6X7Y8Z9A5', fee: '7.77', remark: '', orderId: 'order-4' },
+  { id: 'P005', paymentNo: 'PAY-20260419-005', channelOrderNo: 'TT-UK-20260419-0654', channel: 'TikTok Shop', shopName: 'AeroDry UK', currency: 'GBP', amount: '156.00', payMethod: 'Credit Card', payTime: '2026-04-19 16:35:08', reconcileStatus: '有差异', transactionNo: 'txn_7B8C9D0E6', fee: '4.68', remark: '金额差异 $2.00', orderId: 'order-5' },
+  { id: 'P006', paymentNo: 'PAY-20260418-006', channelOrderNo: 'SHOP-JP-20260418-0987', channel: 'Shopify', shopName: 'UrbanTrail JP', currency: 'JPY', amount: '12500', payMethod: 'Credit Card', payTime: '2026-04-18 14:12:30', reconcileStatus: '已确认', transactionNo: 'txn_8F9G0H1I7', fee: '375', remark: '', orderId: 'order-6' }
 ]);
 
 const loading = ref(false);
@@ -206,8 +209,15 @@ const openReconcile = async () => {
   for (const item of selected.value) {
     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, { 
+        orderStatus: 'paid',
+        paymentStatus: 'paid',
+        paidAt: new Date().toISOString()
+      } as Partial<OrderItem>);
+    }
   }
-  ElMessage.success('对账已完成');
+  ElMessage.success('对账已完成,订单状态已同步更新');
 };
 
 const doExport = () => {

+ 21 - 6
src/views/finance/RefundView.vue

@@ -123,10 +123,13 @@
 <script setup lang="ts">
 import { computed, onMounted, ref } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { api } from '@/api/services';
+import type { OrderItem } from '@/types/page';
 
 interface RefundItem {
   refundNo: string;
   orderNo: string;
+  orderId?: string;
   channel: string;
   currency: string;
   amount: string;
@@ -140,11 +143,11 @@ interface RefundItem {
 }
 
 const items = ref<RefundItem[]>([
-  { refundNo: 'RF-20260420-001', orderNo: 'OMS-20260419-0012', channel: 'Shopify', currency: 'USD', amount: '128.00', refundMethod: '原路退回', reason: '商品破损', status: '待退款', applyTime: '2026-04-20 10:30:00', refundTime: '', channelRefundNo: '', remark: '' },
-  { refundNo: 'RF-20260419-002', orderNo: 'OMS-20260419-0017', channel: 'TikTok Shop', currency: 'GBP', amount: '65.00', refundMethod: '原路退回', reason: '与描述不符', status: '已退款', applyTime: '2026-04-19 21:15:00', refundTime: '2026-04-19 22:30:00', channelRefundNo: 'TT-RF-987654', remark: '' },
-  { refundNo: 'RF-20260419-003', orderNo: 'OMS-20260418-0008', channel: 'Shopify', currency: 'USD', amount: '129.00', refundMethod: '原路退回', reason: '错发商品', status: '退款失败', applyTime: '2026-04-18 15:20:00', refundTime: '', channelRefundNo: '', remark: '渠道返回错误码E102' },
-  { refundNo: 'RF-20260418-004', orderNo: 'OMS-20260418-0015', channel: 'TikTok Shop', currency: 'GBP', amount: '90.00', refundMethod: '原路退回', reason: '超时未发货', status: '已退款', applyTime: '2026-04-18 12:00:00', refundTime: '2026-04-18 14:00:00', channelRefundNo: 'TT-RF-987653', remark: '' },
-  { refundNo: 'RF-20260417-005', orderNo: 'OMS-20260417-0025', channel: 'Shopify', currency: 'USD', amount: '89.00', refundMethod: '原路退回', reason: '买家取消', status: '已退款', applyTime: '2026-04-17 09:30:00', refundTime: '2026-04-17 10:45:00', channelRefundNo: 'SHOP-RF-456789', remark: '' }
+  { refundNo: 'RF-20260420-001', orderNo: 'OMS-20260419-0012', orderId: 'order-1', channel: 'Shopify', currency: 'USD', amount: '128.00', refundMethod: '原路退回', reason: '商品破损', status: '待退款', applyTime: '2026-04-20 10:30:00', refundTime: '', channelRefundNo: '', remark: '' },
+  { refundNo: 'RF-20260419-002', orderNo: 'OMS-20260419-0017', orderId: 'order-2', channel: 'TikTok Shop', currency: 'GBP', amount: '65.00', refundMethod: '原路退回', reason: '与描述不符', status: '已退款', applyTime: '2026-04-19 21:15:00', refundTime: '2026-04-19 22:30:00', channelRefundNo: 'TT-RF-987654', remark: '' },
+  { refundNo: 'RF-20260419-003', orderNo: 'OMS-20260418-0008', orderId: 'order-3', channel: 'Shopify', currency: 'USD', amount: '129.00', refundMethod: '原路退回', reason: '错发商品', status: '退款失败', applyTime: '2026-04-18 15:20:00', refundTime: '', channelRefundNo: '', remark: '渠道返回错误码E102' },
+  { refundNo: 'RF-20260418-004', orderNo: 'OMS-20260418-0015', orderId: 'order-4', channel: 'TikTok Shop', currency: 'GBP', amount: '90.00', refundMethod: '原路退回', reason: '超时未发货', status: '已退款', applyTime: '2026-04-18 12:00:00', refundTime: '2026-04-18 14:00:00', channelRefundNo: 'TT-RF-987653', remark: '' },
+  { refundNo: 'RF-20260417-005', orderNo: 'OMS-20260417-0025', orderId: 'order-5', channel: 'Shopify', currency: 'USD', amount: '89.00', refundMethod: '原路退回', reason: '买家取消', status: '已退款', applyTime: '2026-04-17 09:30:00', refundTime: '2026-04-17 10:45:00', channelRefundNo: 'SHOP-RF-456789', remark: '' }
 ]);
 
 const loading = ref(false);
@@ -182,6 +185,12 @@ const retryRefund = async (row: RefundItem) => {
   await ElMessageBox.confirm('确认重新发起退款?', '重试退款');
   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, { 
+      refundStatus: '部分退款',
+      orderStatus: 'refunded'
+    } as Partial<OrderItem>);
+  }
   ElMessage.success('退款已重新发起');
 };
 
@@ -191,8 +200,14 @@ const openBatchRefund = async () => {
   for (const item of selected.value) {
     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, { 
+        refundStatus: '部分退款',
+        orderStatus: 'refunded'
+      } as Partial<OrderItem>);
+    }
   }
-  ElMessage.success('批量退款已发起');
+  ElMessage.success('批量退款已发起,订单退款状态已更新');
 };
 
 onMounted(loadData);

+ 98 - 4
src/views/order/OrderAfterSaleView.vue

@@ -181,7 +181,7 @@
 import { computed, onMounted, ref } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { api } from '@/api/services';
-import type { AfterSaleItem } from '@/types/page';
+import type { AfterSaleItem, OrderItem } from '@/types/page';
 
 const items = ref<AfterSaleItem[]>([]);
 const loading = ref(false);
@@ -254,7 +254,13 @@ const confirmAudit = async () => {
 const doRefund = async (row: AfterSaleItem) => {
   await ElMessageBox.confirm(`确认对 ${row.afterSaleNo} 发起退款 ${row.amount}?`);
   await api.updateAfterSale(row.id, { refundStatus: '已退款' } as Partial<AfterSaleItem>);
-  ElMessage.success('退款已发起');
+  if (row.orderId) {
+    await api.updateOrder(row.orderId, { 
+      refundStatus: '已退款',
+      orderStatus: 'refunded'
+    } as Partial<OrderItem>);
+  }
+  ElMessage.success('退款已发起,订单状态已更新');
   loadData();
 };
 
@@ -276,7 +282,36 @@ const confirmReturnTracking = async () => {
 const confirmReceipt = async (row: AfterSaleItem) => {
   await ElMessageBox.confirm(`确认已收到 ${row.afterSaleNo} 的退货并入库?`);
   await api.updateAfterSale(row.id, { refundStatus: '已退款' } as Partial<AfterSaleItem>);
-  ElMessage.success('已确认入库并完成退款');
+  
+  if (row.orderId) {
+    const orderRes = await api.getOrder(row.orderId);
+    const order = orderRes;
+    for (const item of order.items || []) {
+      const invRes = await api.getInventory();
+      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, {
+          available: newAvailable,
+          locked: newLocked
+        });
+        await api.createInventoryLog({
+          source: '退货入库',
+          relatedOrder: order.orderNo,
+          operator: '客服',
+          quantity: item.qty,
+          afterQty: newAvailable,
+          time: new Date().toISOString()
+        });
+      }
+    }
+    await api.updateOrder(row.orderId, { 
+      refundStatus: '已退款',
+      orderStatus: 'refunded'
+    } as Partial<OrderItem>);
+  }
+  ElMessage.success('已确认入库并完成退款,库存已回退');
   loadData();
 };
 
@@ -291,9 +326,68 @@ const openResend = (row: AfterSaleItem) => {
 
 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) 
+    : null;
+  
+  const newOrderNo = `OMS-RESEND-${Date.now()}`;
+  const newOrderData: Partial<OrderItem> = {
+    orderNo: newOrderNo,
+    channel: originalOrder?.channel || 'Shopify',
+    orderStatus: 'paid',
+    paymentStatus: 'paid',
+    shippingStatus: 'unshipped',
+    buyer: originalOrder?.buyer || '',
+    buyerCountry: originalOrder?.buyerCountry || 'US',
+    receiverName: originalOrder?.receiverName || '',
+    receiverPhone: originalOrder?.receiverPhone || '',
+    receiverCountry: originalOrder?.receiverCountry || 'US',
+    receiverState: originalOrder?.receiverState || '',
+    receiverCity: originalOrder?.receiverCity || '',
+    receiverDistrict: originalOrder?.receiverDistrict || '',
+    receiverPostalCode: originalOrder?.receiverPostalCode || '',
+    receiverAddress: originalOrder?.receiverAddress || '',
+    currency: originalOrder?.currency || 'USD',
+    warehouse: resendWarehouse.value,
+    originalOrderId: resendTarget.value?.orderId,
+    items: [{
+      sku: resendSku.value,
+      skuId: resendSku.value,
+      productTitle: `补发商品 - ${resendSku.value}`,
+      qty: resendQty.value,
+      price: '0.00',
+      costPrice: '0.00',
+      profit: '0.00',
+      profitRate: 0,
+      subtotal: '0.00'
+    }]
+  };
+  
+  await api.createOrder(newOrderData);
+  
+  const invRes = await api.getInventory();
+  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, {
+      available: newAvailable,
+      locked: newLocked
+    });
+    await api.createInventoryLog({
+      source: '补发单锁定',
+      relatedOrder: newOrderNo,
+      operator: '客服',
+      quantity: -resendQty.value,
+      afterQty: newAvailable,
+      time: new Date().toISOString()
+    });
+  }
+  
   await api.updateAfterSale(resendTarget.value!.id, { refundStatus: '已补发' } as Partial<AfterSaleItem>);
   resendDialog.value = false;
-  ElMessage.success('补发单已生成');
+  ElMessage.success(`补发单 ${newOrderNo} 已生成,库存已扣减`);
   loadData();
 };
 

+ 887 - 166
src/views/order/OrderDetailView.vue

@@ -1,101 +1,434 @@
 <template>
   <div class="app-page">
-    <!-- 订单摘要 -->
-    <section class="glass-card section-card">
-      <div class="table-toolbar">
-        <h2 style="margin:0">订单详情 — {{ order.orderNo }}</h2>
-        <div class="chip-list">
-          <el-button @click="$router.back()">返回列表</el-button>
+    <section class="glass-card section-card header-section">
+      <div class="order-detail-header">
+        <div class="header-left">
+          <div class="header-top">
+            <el-button @click="$router.back()">返回列表</el-button>
+            <el-button type="primary" @click="handleContact">联系买家</el-button>
+          </div>
+          <h2>订单详情 <span class="order-no">{{ order.orderNo }}</span></h2>
+          <div class="header-tags">
+            <el-tag v-if="order.channelOrderNo" type="info">渠道: {{ order.channelOrderNo }}</el-tag>
+            <el-tag v-if="order.priority === 'urgent'" type="danger">加急</el-tag>
+            <el-tag v-if="order.buyerLevel" :type="buyerLevelType(order.buyerLevel)">{{ order.buyerLevel }}</el-tag>
+            <el-tag v-for="tag in (order.orderTags || [])" :key="tag" size="small" type="warning">{{ tag }}</el-tag>
+          </div>
+        </div>
+        <div class="header-actions">
+          <el-button @click="handlePrint">打印面单</el-button>
+          <el-button @click="handlePrintInvoice">打印发货单</el-button>
         </div>
       </div>
-      <div class="stat-grid" style="margin-top:16px">
+      <div class="stat-grid">
+        <article class="stat-card">
+          <div class="stat-card__icon" :style="{ background: statusColor }">
+            <el-icon><Check /></el-icon>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">订单状态</div>
+            <div class="stat-card__value">{{ statusLabel(order.orderStatus ?? '') }}</div>
+          </div>
+        </article>
         <article class="stat-card">
-          <div class="stat-card__label">订单状态</div>
-          <div class="stat-card__value" style="font-size:22px">{{ statusLabel(order.orderStatus ?? '') }}</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
+            <el-icon><Money /></el-icon>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">实付金额</div>
+            <div class="stat-card__value">{{ order.currency }} {{ order.actualPaid || order.amount }}</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">支付金额</div>
-          <div class="stat-card__value" style="font-size:22px">{{ order.amount }}</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
+            <el-icon><Van /></el-icon>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">发货状态</div>
+            <div class="stat-card__value">{{ order.shippingStatus || '待发货' }}</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">发货状态</div>
-          <div class="stat-card__value" style="font-size:22px">{{ order.shippingStatus }}</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
+            <el-icon><Warning /></el-icon>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">异常标签</div>
+            <el-tag :type="order.exceptionTag === '正常' ? 'success' : 'warning'" size="default">{{ order.exceptionTag || '正常' }}</el-tag>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">异常标签</div>
-          <div class="stat-card__value" style="font-size:22px">
-            <el-tag :type="order.exceptionTag === '正常' ? 'success' : 'warning'">{{ order.exceptionTag }}</el-tag>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
+            <el-icon><OfficeBuilding /></el-icon>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">分配仓库</div>
+            <div class="stat-card__value">{{ order.warehouse || '待分配' }}</div>
           </div>
         </article>
       </div>
     </section>
 
-    <section class="page-grid page-grid--two">
-      <!-- 收货 + 支付信息 -->
-      <article class="glass-card section-card">
-        <h3 style="margin:0 0 16px">收货 & 支付信息</h3>
-        <el-descriptions :column="1" border>
-          <el-descriptions-item label="收货人">{{ order.receiver }}</el-descriptions-item>
-          <el-descriptions-item label="电话">{{ order.phone }}</el-descriptions-item>
-          <el-descriptions-item label="地址">{{ order.address }}</el-descriptions-item>
-          <el-descriptions-item label="支付方式">{{ order.payMethod }}</el-descriptions-item>
-          <el-descriptions-item label="支付时间">{{ order.payTime }}</el-descriptions-item>
-          <el-descriptions-item label="渠道">{{ order.channel }}</el-descriptions-item>
-          <el-descriptions-item label="仓库">{{ order.warehouse }}</el-descriptions-item>
-        </el-descriptions>
-      </article>
-
-      <!-- 操作区 -->
-      <article class="glass-card section-card">
-        <h3 style="margin:0 0 16px">操作</h3>
-        <div class="chip-list" style="margin-bottom:16px">
-          <el-button type="primary" @click="assignDrawer = true" :disabled="order.orderStatus === 'cancelled'">提交仓库</el-button>
-          <el-button @click="lockInventory" :disabled="order.orderStatus === 'cancelled'">锁定库存</el-button>
-          <el-button @click="splitDialog = true" :disabled="order.orderStatus === 'cancelled'">拆单</el-button>
-          <el-button @click="mergeDialog = true" :disabled="order.orderStatus === 'cancelled'">合单</el-button>
-          <el-button type="danger" plain @click="cancelDialog = true" :disabled="order.orderStatus === 'cancelled'">取消订单</el-button>
-        </div>
-        <el-form label-position="top">
-          <el-form-item label="订单备注">
-            <el-input v-model="order.remark" type="textarea" :rows="3" placeholder="客服备注" />
-          </el-form-item>
-        </el-form>
-      </article>
-    </section>
+    <div class="detail-grid">
+      <div class="detail-left">
+        <article class="glass-card section-card">
+          <div class="section-header">
+            <h3>买家信息</h3>
+          </div>
+          <el-descriptions :column="2" border size="default">
+            <el-descriptions-item label="买家ID">{{ order.buyerId }}</el-descriptions-item>
+            <el-descriptions-item label="买家昵称">{{ order.buyer }}</el-descriptions-item>
+            <el-descriptions-item label="邮箱">{{ order.buyerEmail }}</el-descriptions-item>
+            <el-descriptions-item label="电话">{{ order.buyerPhone }}</el-descriptions-item>
+            <el-descriptions-item label="国家">{{ order.buyerCountry }} {{ getCountryFlag(order.buyerCountry) }}</el-descriptions-item>
+            <el-descriptions-item label="会员等级">{{ order.buyerLevel || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="注册时间">{{ order.buyerRegisterTime || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="历史订单">{{ order.buyerOrderCount || 0 }} 单</el-descriptions-item>
+            <el-descriptions-item label="历史消费">{{ order.buyerTotalSpent || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="设备信息">{{ order.device }} / {{ order.browser }} / {{ order.os }}</el-descriptions-item>
+          </el-descriptions>
+        </article>
+
+        <article class="glass-card section-card">
+          <div class="section-header">
+            <h3>收货信息</h3>
+            <el-button link type="primary" @click="editAddressDialog = true" :disabled="order.orderStatus === 'cancelled'">修改地址</el-button>
+          </div>
+          <el-descriptions :column="2" border size="default">
+            <el-descriptions-item label="收货人">
+              <span class="value-bold">{{ order.receiverName }}</span>
+              <el-button link type="primary" size="small" @click="handleCall">拨打电话</el-button>
+            </el-descriptions-item>
+            <el-descriptions-item label="联系电话">{{ order.receiverPhone }}</el-descriptions-item>
+            <el-descriptions-item label="国家/地区">{{ order.receiverCountry }} {{ getCountryFlag(order.receiverCountry) }}</el-descriptions-item>
+            <el-descriptions-item label="州/省">{{ order.receiverState || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="城市">{{ order.receiverCity }}</el-descriptions-item>
+            <el-descriptions-item label="区县">{{ order.receiverDistrict || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="邮编">{{ order.receiverPostalCode || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="详细地址" :span="2">
+              <span class="address-text">{{ order.receiverAddress }}</span>
+            </el-descriptions-item>
+            <el-descriptions-item label="坐标">{{ order.latitude?.toFixed(4) }}, {{ order.longitude?.toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="买家备注">
+              <span class="buyer-remark">{{ order.buyerRemark || '无' }}</span>
+            </el-descriptions-item>
+          </el-descriptions>
+        </article>
+
+        <article class="glass-card section-card">
+          <div class="section-header">
+            <h3>支付信息</h3>
+          </div>
+          <el-descriptions :column="2" border size="default">
+            <el-descriptions-item label="支付方式">{{ order.paymentMethod }}</el-descriptions-item>
+            <el-descriptions-item label="支付时间">{{ order.paymentTime || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="交易流水">{{ order.transactionId || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="订单货币">{{ order.currency }} (汇率: {{ order.exchangeRate || 1 }})</el-descriptions-item>
+            <el-descriptions-item label="商品金额">{{ order.orderAmount || order.amount }}</el-descriptions-item>
+            <el-descriptions-item label="折合CNY">¥{{ order.orderAmountCNY || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="税额">{{ order.taxAmount || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="运费">{{ order.shippingFee || '0.00' }}</el-descriptions-item>
+            <el-descriptions-item label="优惠折扣" :span="2">
+              <span v-if="order.couponCode" class="discount-tag">
+                {{ order.couponCode }} (-{{ order.couponDiscount || order.discountAmount || '0.00' }})
+              </span>
+              <span v-else>-</span>
+            </el-descriptions-item>
+            <el-descriptions-item label="实付金额">
+              <span class="value-bold actual-paid">{{ order.currency }} {{ order.actualPaid || order.amount }}</span>
+            </el-descriptions-item>
+            <el-descriptions-item label="退款金额">{{ order.refundAmount || '0.00' }}</el-descriptions-item>
+          </el-descriptions>
+        </article>
+
+        <article class="glass-card section-card">
+          <div class="section-header">
+            <h3>物流追踪</h3>
+            <el-button link type="primary" @click="trackMore">查看更多</el-button>
+          </div>
+          <div v-if="order.trackingNo" class="tracking-info">
+            <div class="tracking-info__main">
+              <el-tag type="success" size="default">{{ order.carrier || '顺丰速运' }}</el-tag>
+              <span class="tracking-no">{{ order.trackingNo }}</span>
+              <el-button link type="primary" @click="copyTrackingNo">复制</el-button>
+              <el-button link type="primary" @click="openTrackingUrl">查询物流</el-button>
+            </div>
+            <div class="tracking-meta">
+              <span>配送方式: {{ order.shippingMethod || '-' }}</span>
+              <span>仓库: {{ order.warehouse || '-' }}</span>
+              <span>库位: {{ order.warehouseLocation || '-' }}</span>
+            </div>
+            <el-timeline class="track-timeline" v-if="trackingSteps.length">
+              <el-timeline-item v-for="(step, index) in trackingSteps" :key="index" :timestamp="step.time" :type="step.type" :hollow="index === 0">
+                <p class="track-title">{{ step.title }}</p>
+                <p class="track-detail" v-if="step.detail">{{ step.detail }}</p>
+              </el-timeline-item>
+            </el-timeline>
+            <el-empty v-else description="暂无物流信息" />
+          </div>
+          <div v-else class="tracking-empty">
+            <el-empty description="暂无物流信息" />
+            <el-button v-if="order.orderStatus === 'shipped'" type="primary" size="default" @click="addTrackingDialog = true">添加物流信息</el-button>
+          </div>
+        </article>
+
+        <article class="glass-card section-card">
+          <div class="section-header">
+            <h3>营销归因</h3>
+          </div>
+          <el-descriptions :column="2" border size="default">
+            <el-descriptions-item label="来源(UTM)">{{ order.utmSource || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="媒介(UTM)">{{ order.utmMedium || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="活动(UTM)">{{ order.utmCampaign || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="内容(UTM)">{{ order.utmContent || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="关键词(UTM)">{{ order.utmTerm || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="下单IP">{{ order.ip || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="IP归属国">{{ order.ipCountry || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="落地页">{{ order.landingPage || '-' }}</el-descriptions-item>
+          </el-descriptions>
+        </article>
+      </div>
+
+      <div class="detail-right">
+        <article class="glass-card section-card">
+          <h3>快捷操作</h3>
+          <div class="action-menu">
+            <el-dropdown trigger="click" @command="handleActionCommand">
+              <el-button type="primary">
+                订单操作 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item command="assign" :disabled="order.orderStatus === 'cancelled'">
+                    <el-icon><Van /></el-icon> 提交仓库
+                  </el-dropdown-item>
+                  <el-dropdown-item command="lock" :disabled="order.orderStatus === 'cancelled'">
+                    <el-icon><Lock /></el-icon> 锁定库存
+                  </el-dropdown-item>
+                  <el-dropdown-item command="split" :disabled="order.orderStatus === 'cancelled'">
+                    <el-icon><Connection /></el-icon> 拆单
+                  </el-dropdown-item>
+                  <el-dropdown-item command="merge" :disabled="order.orderStatus === 'cancelled'">
+                    <el-icon><FolderMerged /></el-icon> 合单
+                  </el-dropdown-item>
+                  <el-dropdown-item command="address" :disabled="order.orderStatus === 'cancelled'">
+                    <el-icon><Edit /></el-icon> 修改地址
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+
+            <el-dropdown trigger="click" @command="handlePrintCommand">
+              <el-button>
+                打印 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item command="label">
+                    <el-icon><Printer /></el-icon> 打印面单
+                  </el-dropdown-item>
+                  <el-dropdown-item command="invoice">
+                    <el-icon><Document /></el-icon> 打印发货单
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
 
-    <!-- 商品明细 -->
-    <section class="glass-card section-card">
-      <h3 style="margin:0 0 16px">商品明细</h3>
-      <el-table :data="lineItems" stripe>
-        <el-table-column prop="sku" label="SKU" width="180" />
-        <el-table-column prop="title" label="商品" min-width="250" />
-        <el-table-column prop="qty" label="数量" width="80" />
-        <el-table-column prop="unitPrice" label="单价" width="100" />
-        <el-table-column prop="subtotal" label="小计" width="100" />
+            <el-dropdown trigger="click" @command="handleRefundCommand">
+              <el-button type="success">
+                退款 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item command="refund" :disabled="!canRefund">
+                    <el-icon><Money /></el-icon> 发起退款
+                  </el-dropdown-item>
+                  <el-dropdown-item command="viewRefund">
+                    <el-icon><Tickets /></el-icon> 查看退款记录
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+
+            <el-button type="danger" plain @click="cancelDialog = true" :disabled="order.orderStatus === 'cancelled'">
+              取消订单
+            </el-button>
+          </div>
+        </article>
+
+        <article class="glass-card section-card">
+          <h3>订单关联</h3>
+          <el-descriptions :column="1" border size="default">
+            <el-descriptions-item label="母订单">{{ order.parentOrderId || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="子订单">{{ order.childOrderIds?.join(', ') || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="合单订单">{{ order.mergeOrderId || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="关联订单">{{ order.relatedOrderId || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="原订单(退款/重发)">{{ order.originalOrderId || '-' }}</el-descriptions-item>
+          </el-descriptions>
+        </article>
+
+        <article class="glass-card section-card">
+          <h3>客服信息</h3>
+          <el-descriptions :column="1" border size="default">
+            <el-descriptions-item label="处理人">{{ order.handler || '待分配' }}</el-descriptions-item>
+            <el-descriptions-item label="处理组">{{ order.handlerGroup || '-' }}</el-descriptions-item>
+          </el-descriptions>
+        </article>
+
+        <article class="glass-card section-card">
+          <h3>内部备注</h3>
+          <el-input v-model="order.internalRemark" type="textarea" :rows="3" placeholder="客服内部备注信息" />
+          <el-button type="primary" size="default" @click="saveRemark" class="save-btn">保存备注</el-button>
+        </article>
+
+        <article class="glass-card section-card">
+          <h3>操作日志</h3>
+          <el-timeline>
+            <el-timeline-item v-for="(log, index) in operationLogs" :key="index" :timestamp="log.time" :type="log.type" :hollow="index === 0">
+              <p class="log-title">{{ log.title }}</p>
+              <p class="log-detail" v-if="log.operator">操作人: {{ log.operator }} ({{ log.operatorRole }})</p>
+            </el-timeline-item>
+          </el-timeline>
+        </article>
+      </div>
+    </div>
+
+    <section class="glass-card section-card products-section">
+      <div class="section-header">
+        <h3>商品明细 ({{ order.items?.length || 0 }} 件)</h3>
+      </div>
+      <el-table :data="order.items" stripe>
+        <el-table-column prop="sku" label="SKU编码" width="160">
+          <template #default="{ row }">
+            <span class="sku-code">{{ row.sku }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="productTitle" label="商品信息" min-width="260">
+          <template #default="{ row }">
+            <div class="product-info">
+              <el-avatar v-if="row.productImage" :src="row.productImage" :size="56" shape="square" />
+              <div v-else class="product-image-placeholder">暂无图</div>
+              <div class="product-detail">
+                <div class="product-title">{{ row.productTitle }}</div>
+                <div class="product-category">{{ row.categoryName }} | {{ row.skuId }}</div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="specs" label="规格" width="160">
+          <template #default="{ row }">
+            <div class="specs-list">
+              <el-tag v-for="spec in row.specs" :key="spec.specName" size="small">{{ spec.specValue }}</el-tag>
+              <span v-if="!row.specs || row.specs.length === 0">-</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="barcode" label="条码" width="140">
+          <template #default="{ row }">
+            <span class="barcode">{{ row.barcode || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="qty" label="数量" width="90" align="center">
+          <template #default="{ row }">
+            <span class="qty">{{ row.qty }}</span>
+            <div class="qty-detail" v-if="row.shippedQty || row.returnedQty">
+              <span>已发: {{ row.shippedQty || 0 }}</span>
+              <span>退: {{ row.returnedQty || 0 }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="price" label="单价" width="100" align="right">
+          <template #default="{ row }">
+            <span>{{ order.currency }} {{ row.price }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="costPrice" label="成本价" width="100" align="right">
+          <template #default="{ row }">
+            <span class="cost-price">{{ order.currency }} {{ row.costPrice }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="profit" label="利润" width="100" align="right">
+          <template #default="{ row }">
+            <span class="profit">{{ order.currency }} {{ row.profit }}</span>
+            <div class="profit-rate">{{ row.profitRate }}%</div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="subtotal" label="小计" width="100" align="right">
+          <template #default="{ row }">
+            <span class="subtotal">{{ order.currency }} {{ row.subtotal }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="inventory" label="库存" width="150" align="center">
+          <template #default="{ row }">
+            <div class="inventory-info">
+              <span class="inv-available">可用: {{ row.available }}</span>
+              <span class="inv-locked">锁定: {{ row.locked }}</span>
+              <span class="inv-transit">在途: {{ row.inTransit }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="giftFlag" label="赠品" width="80" align="center">
+          <template #default="{ row }">
+            <el-tag v-if="row.giftFlag" type="warning" size="small">赠品</el-tag>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
       </el-table>
+      <div class="amount-summary">
+        <div class="summary-row">
+          <span>商品总额:</span>
+          <span>{{ order.currency }} {{ order.orderAmount || order.amount }}</span>
+        </div>
+        <div class="summary-row">
+          <span>税额:</span>
+          <span>{{ order.currency }} {{ order.taxAmount || '0.00' }}</span>
+        </div>
+        <div class="summary-row">
+          <span>运费:</span>
+          <span>{{ order.currency }} {{ order.shippingFee || '0.00' }}</span>
+        </div>
+        <div class="summary-row" v-if="order.discountAmount && parseFloat(order.discountAmount) > 0">
+          <span>优惠:</span>
+          <span class="discount">-{{ order.currency }} {{ order.discountAmount }}</span>
+        </div>
+        <div class="summary-row" v-if="order.couponDiscount && parseFloat(order.couponDiscount) > 0">
+          <span>优惠券:</span>
+          <span class="discount">-{{ order.currency }} {{ order.couponDiscount }} ({{ order.couponCode }})</span>
+        </div>
+        <div class="summary-row summary-row--total">
+          <span>实付金额:</span>
+          <span class="total-amount">{{ order.currency }} {{ order.actualPaid || order.amount }}</span>
+        </div>
+        <div class="summary-row summary-row--profit">
+          <span>订单利润:</span>
+          <span class="profit-amount">{{ order.currency }} {{ totalProfit }}</span>
+          <span class="profit-rate">({{ totalProfitRate }}%)</span>
+        </div>
+      </div>
     </section>
 
-    <!-- 状态时间线 -->
-    <section class="glass-card section-card">
-      <h3 style="margin:0 0 16px">订单轨迹</h3>
-      <el-timeline>
-        <el-timeline-item v-for="item in timeline" :key="item.time" :timestamp="item.time" :type="item.type">
-          <strong>{{ item.title }}</strong>
-          <div style="color:var(--cb-text-soft);margin-top:4px">{{ item.summary }}</div>
-        </el-timeline-item>
-      </el-timeline>
+    <section class="glass-card section-card package-section">
+      <div class="section-header">
+        <h3>包裹信息</h3>
+      </div>
+      <el-descriptions :column="4" border size="default">
+        <el-descriptions-item label="重量">{{ order.weight ? order.weight.toFixed(2) + ' kg' : '-' }}</el-descriptions-item>
+        <el-descriptions-item label="体积">L: {{ order.length || '-' }}cm x W: {{ order.width || '-' }}cm x H: {{ order.height || '-' }}cm</el-descriptions-item>
+        <el-descriptions-item label="仓库">{{ order.warehouse || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="库位">{{ order.warehouseLocation || '-' }}</el-descriptions-item>
+      </el-descriptions>
     </section>
 
-    <!-- 取消弹窗 -->
     <el-dialog v-model="cancelDialog" title="取消订单" width="520px">
       <el-alert title="取消订单需二次确认,操作不可撤销" type="warning" :closable="false" style="margin-bottom:16px" />
       <el-form label-position="top">
         <el-form-item label="取消原因" required>
           <el-select v-model="cancelReason" style="width:100%">
-            <el-option label="买家申请取消" value="买家申请取消" />
-            <el-option label="地址异常无法发货" value="地址异常无法发货" />
-            <el-option label="库存不足" value="库存不足" />
-            <el-option label="其他原因" value="其他原因" />
+            <el-option label="买家申请取消" value="buyer_cancel" />
+            <el-option label="地址异常无法发货" value="address_error" />
+            <el-option label="库存不足" value="out_of_stock" />
+            <el-option label="商品缺货" value="goods_unavailable" />
+            <el-option label="其他原因" value="other" />
           </el-select>
         </el-form-item>
         <el-form-item label="补充说明">
@@ -108,30 +441,37 @@
       </template>
     </el-dialog>
 
-    <!-- 拆单弹窗 -->
     <el-dialog v-model="splitDialog" title="拆单处理" width="620px">
-      <el-table :data="lineItems">
-        <el-table-column prop="sku" label="SKU" min-width="180" />
-        <el-table-column prop="title" label="商品" min-width="200" />
-        <el-table-column prop="qty" label="原数量" width="90" />
-        <el-table-column label="拆单数量" width="140">
+      <el-alert title="拆单后原订单将变更为主订单,新订单将从原订单拆分" type="info" :closable="false" style="margin-bottom:16px" />
+      <el-table :data="order.items">
+        <el-table-column prop="sku" label="SKU" min-width="160" />
+        <el-table-column prop="productTitle" label="商品" min-width="180" show-overflow-tooltip />
+        <el-table-column prop="qty" label="原数量" width="90" align="center" />
+        <el-table-column label="主单数量" width="120">
+          <template #default="{ row }">
+            <el-input-number v-model="row.mainQty" :min="0" :max="row.qty" size="small" @change="updateSplitQty(row)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="新单数量" width="100">
           <template #default="{ row }">
-            <el-input-number v-model="row.splitQty" :min="0" :max="row.qty" size="small" />
+            <span class="split-qty">{{ row.qty - (row.mainQty || 0) }}</span>
           </template>
         </el-table-column>
       </el-table>
       <template #footer>
         <el-button @click="splitDialog = false">取消</el-button>
-        <el-button type="primary" @click="confirmSplit">生成拆单方案</el-button>
+        <el-button type="primary" @click="confirmSplit">确认拆单</el-button>
       </template>
     </el-dialog>
 
-    <!-- 合单弹窗 -->
     <el-dialog v-model="mergeDialog" title="合单处理" width="480px">
       <el-form label-position="top">
-        <el-form-item label="目标订单号">
+        <el-form-item label="目标订单号" required>
           <el-input v-model="mergeTarget" placeholder="请输入待合并的订单号" />
         </el-form-item>
+        <el-form-item>
+          <el-alert title="合单要求:两个订单买家信息一致,且均未发货" type="warning" :closable="false" />
+        </el-form-item>
       </el-form>
       <template #footer>
         <el-button @click="mergeDialog = false">取消</el-button>
@@ -139,17 +479,50 @@
       </template>
     </el-dialog>
 
-    <!-- 分配仓库抽屉 -->
+    <el-dialog v-model="editAddressDialog" title="修改收货地址" width="520px">
+      <el-form :model="editAddress" label-position="top">
+        <el-form-item label="收货人" required>
+          <el-input v-model="editAddress.receiverName" placeholder="请输入收货人姓名" />
+        </el-form-item>
+        <el-form-item label="联系电话" required>
+          <el-input v-model="editAddress.receiverPhone" placeholder="请输入联系电话" />
+        </el-form-item>
+        <el-form-item label="国家" required>
+          <el-input v-model="editAddress.receiverCountry" placeholder="请输入国家" />
+        </el-form-item>
+        <el-form-item label="州/省">
+          <el-input v-model="editAddress.receiverState" placeholder="请输入州/省" />
+        </el-form-item>
+        <el-form-item label="城市" required>
+          <el-input v-model="editAddress.receiverCity" placeholder="请输入城市" />
+        </el-form-item>
+        <el-form-item label="邮编">
+          <el-input v-model="editAddress.receiverPostalCode" placeholder="请输入邮编" />
+        </el-form-item>
+        <el-form-item label="详细地址" required>
+          <el-input v-model="editAddress.receiverAddress" type="textarea" :rows="3" placeholder="请输入详细地址" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="editAddressDialog = false">取消</el-button>
+        <el-button type="primary" @click="confirmEditAddress">确认修改</el-button>
+      </template>
+    </el-dialog>
+
     <el-drawer v-model="assignDrawer" title="分配仓库" size="400px">
       <el-form label-position="top">
-        <el-form-item label="选择仓库">
+        <el-form-item label="选择仓库" required>
           <el-select v-model="order.warehouse" style="width:100%">
-            <el-option label="深圳仓" value="深圳仓" />
-            <el-option label="义乌仓" value="义乌仓" />
+            <el-option label="深圳南山仓" value="深圳南山仓" />
+            <el-option label="义乌商贸仓" value="义乌商贸仓" />
+            <el-option label="洛杉矶海外仓" value="洛杉矶海外仓" />
           </el-select>
         </el-form-item>
+        <el-form-item label="库位">
+          <el-input v-model="order.warehouseLocation" placeholder="可选填写库位号" />
+        </el-form-item>
         <el-form-item label="仓库备注">
-          <el-input v-model="warehouseRemark" type="textarea" :rows="4" />
+          <el-input v-model="warehouseRemark" type="textarea" :rows="4" placeholder="可选填写仓库操作备注" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -161,107 +534,258 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, reactive, ref } from 'vue';
+import { onMounted, reactive, ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { api } from '@/api/services';
-import type { OrderItem } from '@/types/page';
+import type { OrderItem, OrderProductItem } from '@/types/page';
 
 const route = useRoute();
 const cancelDialog = ref(false);
 const splitDialog = ref(false);
 const mergeDialog = ref(false);
 const assignDrawer = ref(false);
+const editAddressDialog = ref(false);
 const cancelReason = ref('');
 const cancelRemark = ref('');
 const mergeTarget = ref('');
 const warehouseRemark = ref('');
 
-const order = reactive<Partial<OrderItem> & { address?: string; payMethod?: string; payTime?: string; phone?: string; receiver?: string; remark?: string }>({
-  id: '', orderNo: '', channel: '', buyer: '', amount: '', paymentStatus: '', orderStatus: '', shippingStatus: '', warehouse: '', exceptionTag: '', createdAt: '',
-  receiver: '', phone: '', address: '', payMethod: '', payTime: '', remark: ''
+const canRefund = computed(() => ['paid', 'allocated', 'shipped'].includes(order.orderStatus || ''));
+
+const countryFlags: Record<string, string> = {
+  'US': '🇺🇸', 'UK': '🇬🇧', 'GB': '🇬🇧', 'JP': '🇯🇵', 'DE': '🇩🇪', 'FR': '🇫🇷',
+  'CA': '🇨🇦', 'AU': '🇦🇺', 'IT': '🇮🇹', 'ES': '🇪🇸', 'NL': '🇳🇱', 'BR': '🇧🇷',
+  'MX': '🇲🇽', 'KR': '🇰🇷', 'CN': '🇨🇳', 'HK': '🇭🇰', 'TW': '🇹🇼', 'SG': '🇸🇬'
+};
+
+const getCountryFlag = (country: string): string => countryFlags[country] || '🌍';
+
+const statusColor = computed(() => {
+  const colors: Record<string, string> = {
+    created: 'linear-gradient(135deg, #909399 0%, #b1b3b8 100%)',
+    paid: 'linear-gradient(135deg, #409eff 0%, #66b1ff 100%)',
+    allocated: 'linear-gradient(135deg, #e6a23c 0%, #ebb563 100%)',
+    shipped: 'linear-gradient(135deg, #67c23a 0%, #85ce61 100%)',
+    delivered: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+    completed: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+    cancelled: 'linear-gradient(135deg, #f56c6c 0%, #f78989 100%)',
+    refunded: 'linear-gradient(135deg, #f56c6c 0%, #f78989 100%)'
+  };
+  return colors[order.orderStatus || ''] || colors.created;
 });
 
-const lineItems = ref<{ sku: string; title: string; qty: number; splitQty: number; unitPrice: string; subtotal: string }[]>([]);
+const order = reactive<OrderItem>({
+  id: '', orderNo: '', channelOrderNo: '', channel: '', orderStatus: '', shippingStatus: '', paymentStatus: '', exceptionTag: '', priority: 'normal',
+  createdAt: '', updatedAt: '', paidAt: '', shippedAt: '', deliveredAt: '',
+  buyerId: '', buyer: '', buyerEmail: '', buyerPhone: '', buyerCountry: '', buyerLevel: '', buyerTags: [], buyerRegisterTime: '', buyerOrderCount: 0, buyerTotalSpent: '',
+  receiverName: '', receiverPhone: '', receiverCountry: '', receiverState: '', receiverCity: '', receiverDistrict: '', receiverPostalCode: '', receiverAddress: '', receiverAddressLang: '', latitude: 0, longitude: 0,
+  transactionId: '', paymentMethod: '', paymentTime: '', currency: 'USD', exchangeRate: 1, orderAmount: '', orderAmountCNY: '', taxAmount: '', shippingFee: '', discountAmount: '', couponCode: '', couponDiscount: '', actualPaid: '', refundAmount: '', refundStatus: '',
+  shippingMethod: '', carrier: '', trackingNo: '', trackingUrl: '', warehouse: '', warehouseLocation: '', estimatedDelivery: '', weight: 0, length: 0, width: 0, height: 0,
+  utmSource: '', utmMedium: '', utmCampaign: '', utmContent: '', utmTerm: '', referrerUrl: '', landingPage: '', ip: '', ipCountry: '', device: '', browser: '', os: '',
+  parentOrderId: '', childOrderIds: [], mergeOrderId: '', relatedOrderId: '', originalOrderId: '',
+  handler: '', handlerGroup: '', orderTags: [], internalRemark: '', buyerRemark: '',
+  itemCount: 0, amount: '', items: [], timeline: [], logs: []
+});
 
-const timeline = ref<{ time: string; title: string; summary: string; type: string }[]>([]);
+const operationLogs = ref<{ time: string; title: string; type: string; operator?: string; operatorRole?: string }[]>([]);
+const trackingSteps = ref<{ time: string; title: string; detail?: string; type: string }[]>([]);
+
+const editAddress = reactive({ receiverName: '', receiverPhone: '', receiverCountry: '', receiverState: '', receiverCity: '', receiverPostalCode: '', receiverAddress: '' });
 
 const statusLabel = (s: string) => {
   const map: Record<string, string> = { created: '待支付', paid: '已支付', allocated: '已分配', shipped: '已发货', delivered: '已签收', completed: '已完成', cancelled: '已取消', refunded: '已退款' };
   return map[s] || s;
 };
 
+const buyerLevelType = (level: string) => {
+  const map: Record<string, string> = { 'VIP': 'danger', '黄金': 'warning', '普通': 'info' };
+  return map[level] || 'info';
+};
+
+const refundStatusType = (status: string) => {
+  const map: Record<string, string> = { '无退款': 'info', '部分退款': 'warning', '已全额退款': 'danger' };
+  return map[status] || 'info';
+};
+
+const totalProfit = computed(() => {
+  if (!order.items || order.items.length === 0) return '0.00';
+  return order.items.reduce((sum, item) => sum + (parseFloat(item.profit) || 0), 0).toFixed(2);
+});
+
+const totalProfitRate = computed(() => {
+  if (!order.items || order.items.length === 0) return '0';
+  const totalAmount = order.items.reduce((sum, item) => sum + (parseFloat(item.subtotal) || 0), 0);
+  const totalCost = order.items.reduce((sum, item) => sum + (parseFloat(item.costPrice) * item.qty || 0), 0);
+  if (totalCost === 0) return '0';
+  return ((parseFloat(totalProfit.value) / totalCost) * 100).toFixed(1);
+});
+
 const loadData = async () => {
   const id = route.query.id as string;
   if (!id) return;
-  const res = await api.getOrder(id);
-  Object.assign(order, res);
-
-  // Dynamic mock detail data based on order number
-  const details: Record<string, Partial<typeof order>> = {
-    'OMS-20260419-0012': { receiver: 'Olivia Zhang', phone: '+1 213-555-4401', address: '1234 Main St, Apt 5B, Los Angeles, CA 90001, US', payMethod: '信用卡', payTime: '2026-04-19 20:43', remark: '地址楼栋不清晰,需要客服复核后再提交仓库。' },
-    'OMS-20260419-0017': { receiver: 'Noah Smith', phone: '+44 20-7946-0958', address: '45 Kensington High St, London, W8 5NP, UK', payMethod: 'TikTok Pay', payTime: '2026-04-19 21:09', remark: '' },
-    'OMS-20260419-0022': { receiver: 'Liam Chen', phone: '+81 3-1234-5678', address: '東京都渋谷区神宮前1-2-3, 150-0001', payMethod: '信用卡', payTime: '2026-04-19 21:32', remark: '' },
-    'OMS-20260418-0008': { receiver: 'Emma Wilson', phone: '+1 310-555-8899', address: '5678 Ocean Ave, Santa Monica, CA 90401, US', payMethod: 'PayPal', payTime: '-', remark: '' },
-    'OMS-20260418-0015': { receiver: 'Sophie Brown', phone: '+44 7911-123456', address: '12 Baker St, Manchester, M1 1AA, UK', payMethod: 'TikTok Pay', payTime: '2026-04-18 10:46', remark: '' },
-  };
-  const detail = details[order.orderNo || ''] || { receiver: '-', phone: '-', address: '-', payMethod: '-', payTime: '-', remark: '' };
-  Object.assign(order, detail);
-
-  // Dynamic line items based on order
-  const lineItemsMap: Record<string, typeof lineItems.value> = {
-    'OMS-20260419-0012': [
-      { sku: 'SKU-LUGG-20-BLK', title: 'TravelFlex Expandable Carry-On / Black', qty: 1, splitQty: 0, unitPrice: '$89.00', subtotal: '$89.00' },
-      { sku: 'SKU-TAG-SET-GRY', title: 'Travel Tag Set / Gray', qty: 2, splitQty: 0, unitPrice: '$19.50', subtotal: '$39.00' }
-    ],
-    'OMS-20260419-0017': [
-      { sku: 'SKU-BAG-ML-BRW', title: 'Classic Leather Tote / Brown', qty: 1, splitQty: 0, unitPrice: '£65.00', subtotal: '£65.00' }
-    ],
-    'OMS-20260419-0022': [
-      { sku: 'SKU-SPRT-YGA-BLU', title: 'Yoga Mat Pro / Blue', qty: 1, splitQty: 0, unitPrice: '¥4,200', subtotal: '¥4,200' },
-      { sku: 'SKU-SPRT-BTL-GRN', title: 'Sports Bottle 750ml / Green', qty: 3, splitQty: 0, unitPrice: '¥1,500', subtotal: '¥4,500' }
-    ],
-    'OMS-20260418-0008': [
-      { sku: 'SKU-LUGG-28-NVY', title: 'TravelFlex Large Check-In / Navy', qty: 1, splitQty: 0, unitPrice: '$129.00', subtotal: '$129.00' }
-    ],
-    'OMS-20260418-0015': [
-      { sku: 'SKU-BAG-BPK-OLV', title: 'Urban Backpack / Olive', qty: 2, splitQty: 0, unitPrice: '£45.00', subtotal: '£90.00' }
-    ],
-  };
-  lineItems.value = lineItemsMap[order.orderNo || ''] || [
-    { sku: 'SKU-GEN-001', title: '通用商品 / Default', qty: 1, splitQty: 0, unitPrice: '-', subtotal: '-' }
+
+  Object.assign(order, generateMockOrderDetail(id));
+};
+
+const generateMockOrderDetail = (id: string): Partial<OrderItem> => {
+  const orderNo = `OMS-20260419-0012`;
+  const items: OrderProductItem[] = [
+    {
+      id: 'item-1',
+      productId: 'prod-001',
+      skuId: 'skuid-001',
+      sku: 'SKU-LUGG-20-BLK',
+      productTitle: 'TravelFlex Expandable Carry-On 20寸 行李箱 / Black 黑色',
+      productImage: '',
+      categoryId: 'cat-001',
+      categoryName: '行李箱',
+      specs: [
+        { specName: '尺寸', specValue: '20寸' },
+        { specName: '颜色', specValue: '黑色' }
+      ],
+      barcode: `BC${Date.now()}001`,
+      qty: 1,
+      price: '89.00',
+      costPrice: '45.00',
+      profit: '44.00',
+      profitRate: 49,
+      subtotal: '89.00',
+      weight: 3.5,
+      available: 45,
+      locked: 5,
+      inTransit: 20,
+      reservedQty: 0,
+      shippedQty: 0,
+      returnedQty: 0,
+      giftFlag: false
+    },
+    {
+      id: 'item-2',
+      productId: 'prod-002',
+      skuId: 'skuid-002',
+      sku: 'SKU-TAG-SET-GRY',
+      productTitle: 'Travel Tag Set 旅行标签牌套装 / Gray 灰色',
+      productImage: '',
+      categoryId: 'cat-002',
+      categoryName: '旅行配件',
+      specs: [
+        { specName: '颜色', specValue: '灰色' }
+      ],
+      barcode: `BC${Date.now()}002`,
+      qty: 2,
+      price: '19.50',
+      costPrice: '8.00',
+      profit: '23.00',
+      profitRate: 59,
+      subtotal: '39.00',
+      weight: 0.2,
+      available: 120,
+      locked: 10,
+      inTransit: 50,
+      reservedQty: 0,
+      shippedQty: 0,
+      returnedQty: 0,
+      giftFlag: false
+    }
   ];
 
-  // Dynamic timeline based on order
-  const timelineMap: Record<string, typeof timeline.value> = {
-    'OMS-20260419-0012': [
-      { time: '2026-04-19 21:16', title: '地址校验告警', summary: '地址楼栋信息不完整,标记为"地址需复核"', type: 'warning' },
-      { time: '2026-04-19 21:10', title: '库存锁定完成', summary: '系统自动锁定 SKU-LUGG-20-BLK × 1, SKU-TAG-SET-GRY × 2', type: 'success' },
-      { time: '2026-04-19 20:43', title: '支付确认', summary: '状态 created → paid,金额 $128.00', type: 'primary' },
-      { time: '2026-04-19 20:42', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-    ],
-    'OMS-20260419-0017': [
-      { time: '2026-04-19 21:12', title: '库存锁定完成', summary: '系统自动锁定 SKU-BAG-ML-BRW × 1', type: 'success' },
-      { time: '2026-04-19 21:09', title: '支付确认', summary: '状态 created → paid,金额 £65.00', type: 'primary' },
-      { time: '2026-04-19 21:08', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-    ],
-    'OMS-20260419-0022': [
-      { time: '2026-04-19 21:35', title: '库存锁定完成', summary: '系统自动锁定 SKU-SPRT-YGA-BLU × 1, SKU-SPRT-BTL-GRN × 3', type: 'success' },
-      { time: '2026-04-19 21:32', title: '支付确认', summary: '状态 created → paid,金额 ¥8,700', type: 'primary' },
-      { time: '2026-04-19 21:31', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-    ],
-    'OMS-20260418-0008': [
-      { time: '2026-04-18 09:15', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-    ],
-    'OMS-20260418-0015': [
-      { time: '2026-04-18 10:48', title: '库存锁定完成', summary: '系统自动锁定 SKU-BAG-BPK-OLV × 2', type: 'success' },
-      { time: '2026-04-18 10:46', title: '支付确认', summary: '状态 created → paid,金额 £90.00', type: 'primary' },
-      { time: '2026-04-18 10:45', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-    ],
+  const amount = items.reduce((sum, item) => sum + parseFloat(item.subtotal), 0);
+
+  return {
+    id,
+    orderNo,
+    channelOrderNo: `CH${Date.now()}`,
+    channel: 'Shopify US',
+    orderStatus: 'paid',
+    shippingStatus: 'unshipped',
+    paymentStatus: 'paid',
+    exceptionTag: '正常',
+    priority: 'normal',
+    createdAt: '2026-04-19 20:42:00',
+    updatedAt: '2026-04-19 21:16:00',
+    paidAt: '2026-04-19 20:43:00',
+    shippedAt: '',
+    deliveredAt: '',
+    buyerId: 'buyer-1001',
+    buyer: 'Olivia Zhang',
+    buyerEmail: 'olivia.zhang@example.com',
+    buyerPhone: '+1 213-555-4401',
+    buyerCountry: 'US',
+    buyerLevel: 'VIP',
+    buyerTags: ['高价值'],
+    buyerRegisterTime: '2025-06-15',
+    buyerOrderCount: 12,
+    buyerTotalSpent: '$1,234.56',
+    receiverName: 'Olivia Zhang',
+    receiverPhone: '+1 213-555-4401',
+    receiverCountry: 'US',
+    receiverState: 'California',
+    receiverCity: 'Los Angeles',
+    receiverDistrict: 'Downtown',
+    receiverPostalCode: '90001',
+    receiverAddress: '1234 Main St, Apt 5B, Los Angeles, CA 90001, United States',
+    receiverAddressLang: '',
+    latitude: 34.0522,
+    longitude: -118.2437,
+    transactionId: `TXN${Date.now()}`,
+    paymentMethod: 'Visa (****4242)',
+    paymentTime: '2026-04-19 20:43:00',
+    currency: 'USD',
+    exchangeRate: 1,
+    orderAmount: amount.toFixed(2),
+    orderAmountCNY: (amount * 7.2).toFixed(2),
+    taxAmount: (amount * 0.08).toFixed(2),
+    shippingFee: '0.00',
+    discountAmount: '5.00',
+    couponCode: 'SAVE10',
+    couponDiscount: '5.00',
+    actualPaid: (amount - 5).toFixed(2),
+    refundAmount: '0.00',
+    refundStatus: '无退款',
+    shippingMethod: '标快',
+    carrier: '顺丰速运',
+    trackingNo: '',
+    trackingUrl: '',
+    warehouse: '深圳南山仓',
+    warehouseLocation: 'A-12-C',
+    estimatedDelivery: '2026-04-25',
+    weight: 3.7,
+    length: 55,
+    width: 35,
+    height: 25,
+    utmSource: 'google',
+    utmMedium: 'cpc',
+    utmCampaign: 'Spring Sale 2026',
+    utmContent: 'Product luggage',
+    utmTerm: 'carry on luggage',
+    referrerUrl: 'https://www.google.com/search?q=carry+on+luggage',
+    landingPage: '/product/luggage-001',
+    ip: '192.168.1.100',
+    ipCountry: 'US',
+    device: 'iPhone',
+    browser: 'Safari',
+    os: 'iOS',
+    parentOrderId: '',
+    childOrderIds: [],
+    mergeOrderId: '',
+    relatedOrderId: '',
+    originalOrderId: '',
+    handler: '陈欣',
+    handlerGroup: '运营组A',
+    orderTags: ['VIP客户'],
+    internalRemark: '地址楼栋不清晰,需要客服复核后再提交仓库',
+    buyerRemark: 'Please deliver to door',
+    itemCount: items.reduce((sum, item) => sum + item.qty, 0),
+    amount: amount.toFixed(2),
+    items,
+    timeline: [],
+    logs: []
   };
-  timeline.value = timelineMap[order.orderNo || ''] || [
-    { time: order.createdAt || '-', title: '订单接入 OMS', summary: `来自 ${order.channel} 的订单推送`, type: 'primary' }
-  ];
+};
+
+const updateSplitQty = (row: OrderProductItem) => {
+  row.mainQty = row.mainQty || 0;
 };
 
 const lockInventory = () => { ElMessage.success('库存已锁定'); };
@@ -271,16 +795,16 @@ const confirmCancel = async () => {
   await ElMessageBox.confirm('确认取消此订单?操作不可撤销。', '二次确认', { type: 'warning' });
   await api.updateOrder(order.id!, { orderStatus: 'cancelled' } as Partial<OrderItem>);
   order.orderStatus = 'cancelled';
-  timeline.value.unshift({ time: '刚刚', title: '订单已取消', summary: cancelReason.value, type: 'danger' });
+  operationLogs.value.unshift({ time: '刚刚', title: '订单已取消', summary: cancelReason.value, type: 'danger', operator: '客服', operatorRole: '客服组' });
   cancelDialog.value = false;
   ElMessage.success('订单已取消');
 };
 
 const confirmSplit = () => {
-  const hasSplit = lineItems.value.some(i => i.splitQty > 0);
-  if (!hasSplit) { ElMessage.warning('请至少为一个 SKU 设置拆单数量'); return; }
+  const hasSplit = order.items?.some(i => (i.mainQty || 0) < i.qty);
+  if (!hasSplit) { ElMessage.warning('请至少设置一个 SKU 的主单数量小于原数量'); return; }
   splitDialog.value = false;
-  timeline.value.unshift({ time: '刚刚', title: '拆单方案已生成', summary: '待确认后生成新发货单', type: 'primary' });
+  operationLogs.value.unshift({ time: '刚刚', title: '拆单方案已生成', summary: '待仓库确认后生成新发货单', type: 'primary', operator: '客服', operatorRole: '客服组' });
   ElMessage.success('拆单方案已生成');
 };
 
@@ -291,11 +815,208 @@ const confirmMerge = () => {
 };
 
 const submitWarehouse = async () => {
-  await api.updateOrder(order.id!, { warehouse: order.warehouse } as Partial<OrderItem>);
+  await api.updateOrder(order.id!, { 
+    warehouse: order.warehouse,
+    warehouseLocation: order.warehouseLocation,
+    orderStatus: 'allocated'
+  } as Partial<OrderItem>);
+  
+  for (const item of order.items || []) {
+    if (item.skuId) {
+      const inventoryRes = await api.getInventory();
+      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, {
+          locked: newLocked,
+          available: newAvailable
+        });
+        await api.createInventoryLog({
+          source: '订单分配锁定',
+          relatedOrder: order.orderNo,
+          operator: '客服',
+          quantity: -item.qty,
+          afterQty: newAvailable,
+          time: new Date().toISOString()
+        });
+      }
+    }
+  }
+  
+  order.orderStatus = 'allocated';
   assignDrawer.value = false;
-  timeline.value.unshift({ time: '刚刚', title: '已提交仓库', summary: `分配至 ${order.warehouse}`, type: 'success' });
-  ElMessage.success('已提交仓库');
+  operationLogs.value.unshift({ time: '刚刚', title: '已提交仓库', summary: `分配至 ${order.warehouse} (${order.warehouseLocation}),库存已锁定`, type: 'success', operator: '客服', operatorRole: '客服组' });
+  ElMessage.success(`已提交仓库,订单状态已更新为已分配,${order.items?.length || 0} 个 SKU 库存已锁定`);
+};
+
+const confirmEditAddress = () => {
+  if (!editAddress.receiverName || !editAddress.receiverPhone || !editAddress.receiverAddress) {
+    ElMessage.warning('请填写完整地址信息');
+    return;
+  }
+  Object.assign(order, {
+    receiverName: editAddress.receiverName,
+    receiverPhone: editAddress.receiverPhone,
+    receiverCountry: editAddress.receiverCountry,
+    receiverState: editAddress.receiverState,
+    receiverCity: editAddress.receiverCity,
+    receiverPostalCode: editAddress.receiverPostalCode,
+    receiverAddress: editAddress.receiverAddress
+  });
+  editAddressDialog.value = false;
+  operationLogs.value.unshift({ time: '刚刚', title: '收货地址已修改', summary: `${editAddress.receiverName}, ${editAddress.receiverCity}`, type: 'warning', operator: '客服', operatorRole: '客服组' });
+  ElMessage.success('地址已修改');
+};
+
+const saveRemark = () => {
+  ElMessage.success('备注已保存');
+};
+
+const handlePrint = () => { ElMessage.success(`正在生成 ${order.orderNo} 的面单...`); };
+const handlePrintInvoice = () => { ElMessage.success(`正在生成 ${order.orderNo} 的发货单...`); };
+const handleContact = () => { ElMessage.info(`正在打开与 ${order.buyer} 的对话窗口...`); };
+const handleRefund = () => { ElMessage.success('正在发起退款流程...'); };
+const handleCall = () => { ElMessage.info(`正在拨打 ${order.receiverPhone}...`); };
+const copyTrackingNo = () => { 
+  if (order.trackingNo) {
+    navigator.clipboard.writeText(order.trackingNo);
+    ElMessage.success('复制成功');
+  }
 };
+const openTrackingUrl = () => { ElMessage.info('正在打开物流追踪页面...'); };
+const trackMore = () => { ElMessage.info('正在加载完整物流信息...'); };
 
-onMounted(loadData);
+const handleActionCommand = (command: string) => {
+  switch (command) {
+    case 'assign': assignDrawer.value = true; break;
+    case 'lock': lockInventory(); break;
+    case 'split': splitDialog.value = true; break;
+    case 'merge': mergeDialog.value = true; break;
+    case 'address': editAddressDialog.value = true; break;
+  }
+};
+
+const handlePrintCommand = (command: string) => {
+  if (command === 'label') {
+    handlePrint();
+  } else if (command === 'invoice') {
+    handlePrintInvoice();
+  }
+};
+
+const handleRefundCommand = (command: string) => {
+  if (command === 'refund') {
+    handleRefund();
+  } else if (command === 'viewRefund') {
+    ElMessage.info('正在打开退款记录...');
+  }
+};
+
+onMounted(() => {
+  loadData();
+  operationLogs.value = [
+    { time: '2026-04-19 21:16', title: '地址校验告警', type: 'warning', operator: '系统', operatorRole: '自动' },
+    { time: '2026-04-19 21:10', title: '库存锁定完成', type: 'success', operator: '系统', operatorRole: '自动' },
+    { time: '2026-04-19 20:43', title: '支付确认', type: 'primary', operator: '支付网关', operatorRole: '自动' },
+    { time: '2026-04-19 20:42', title: '订单接入 OMS', type: 'primary', operator: '渠道同步', operatorRole: '自动' }
+  ];
+});
 </script>
+
+<style scoped>
+.order-detail-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  padding-bottom: 20px;
+  border-bottom: 1px solid #f0f0f0;
+}
+.header-left { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
+.header-left h2 { margin: 0; font-size: 22px; display: flex; align-items: center; gap: 12px; }
+.order-no { color: var(--cb-primary); font-size: 18px; }
+.header-actions { display: flex; gap: 10px; }
+
+.stat-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
+.stat-card { display: flex; align-items: center; gap: 16px; padding: 18px 16px; background: #fafafa; border-radius: 12px; transition: all 0.2s; }
+.stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
+.stat-card__icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: #fff; flex-shrink: 0; }
+.stat-card__content { flex: 1; min-width: 0; }
+.stat-card__label { font-size: 13px; color: #888; margin-bottom: 4px; }
+.stat-card__value { font-size: 17px; font-weight: 600; color: #333; }
+
+.detail-grid { display: grid; grid-template-columns: 1fr 360px; gap: 20px; margin-top: 20px; }
+.detail-left, .detail-right { display: flex; flex-direction: column; gap: 20px; }
+
+.glass-card { padding: 20px; }
+.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }
+.section-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #333; }
+.value-bold { font-weight: 600; }
+.address-text { line-height: 1.7; color: #555; }
+
+.tracking-info__main { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 12px 16px; background: #f8f9fa; border-radius: 8px; }
+.tracking-no { font-family: 'Monaco', 'Menlo', monospace; font-size: 15px; font-weight: 600; letter-spacing: 0.5px; }
+.tracking-meta { display: flex; gap: 20px; font-size: 14px; color: #666; margin-bottom: 20px; }
+.track-timeline { padding: 0 8px; }
+.track-title { margin: 0; font-weight: 600; }
+.track-detail { margin: 6px 0 0; color: #999; font-size: 13px; }
+.tracking-empty { text-align: center; padding: 32px 0; }
+.action-menu { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
+.action-menu .el-dropdown { display: flex; }
+.action-menu .el-button { display: flex; align-items: center; gap: 6px; }
+
+:deep(.el-dropdown-menu__item) {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 10px 16px;
+  font-size: 14px;
+}
+:deep(.el-dropdown-menu__item .el-icon) {
+  font-size: 16px;
+}
+.log-title { margin: 0; font-weight: 600; }
+.log-detail { margin: 6px 0 0; color: #999; font-size: 13px; }
+
+.product-info { display: flex; gap: 14px; align-items: flex-start; }
+.product-image-placeholder { width: 56px; height: 56px; background: #f5f5f5; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #999; flex-shrink: 0; }
+.product-detail { display: flex; flex-direction: column; gap: 5px; }
+.product-title { font-weight: 600; font-size: 14px; line-height: 1.4; }
+.product-category { font-size: 12px; color: #999; }
+.sku-code, .barcode { font-family: 'Monaco', 'Menlo', monospace; font-size: 12px; }
+.specs-list { display: flex; flex-wrap: wrap; gap: 5px; }
+.qty { font-weight: 700; font-size: 17px; }
+.qty-detail { display: flex; gap: 10px; font-size: 11px; color: #999; margin-top: 4px; }
+.cost-price { color: #999; }
+.profit { color: #67c23a; font-weight: 600; }
+.profit-rate { font-size: 11px; color: #67c23a; }
+.subtotal { font-weight: 700; color: var(--cb-accent); }
+.inventory-info { display: flex; flex-direction: column; gap: 3px; font-size: 12px; }
+.inv-available { color: #67c23a; }
+.inv-locked { color: #e6a23c; }
+.inv-transit { color: #409eff; }
+
+.amount-summary { margin-top: 20px; padding: 20px; background: #fafafa; border-radius: 10px; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; }
+.summary-row { display: flex; gap: 48px; font-size: 14px; width: 100%; justify-content: flex-end; }
+.summary-row span:first-child { color: #666; min-width: 90px; text-align: right; }
+.summary-row span:last-child { min-width: 110px; text-align: right; }
+.summary-row--total { font-size: 17px; font-weight: 600; padding-top: 12px; border-top: 1px solid #eee; }
+.total-amount { color: var(--cb-accent); font-size: 20px; }
+.summary-row--profit { color: #67c23a; }
+.profit-amount { font-weight: 600; }
+.profit-rate { color: #67c23a; }
+.discount { color: #67c23a; }
+
+.glass-card.section-card { padding: 24px; }
+
+.header-section { padding: 24px; }
+.header-top { display: flex; gap: 10px; margin-bottom: 12px; }
+.header-tags { display: flex; gap: 8px; flex-wrap: wrap; }
+.buyer-remark { color: #888; font-style: italic; }
+.discount-tag { color: #67c23a; font-weight: 500; }
+.actual-paid { color: var(--cb-accent); font-size: 16px; }
+
+.products-section { margin-top: 20px; }
+.package-section { margin-top: 20px; }
+.save-btn { margin-top: 12px; }
+</style>

+ 492 - 137
src/views/order/OrderListView.vue

@@ -1,107 +1,281 @@
 <template>
   <div class="app-page">
-    <!-- 筛选区 -->
-    <section class="glass-card section-card">
-      <el-form :model="filters" inline class="filter-form">
-        <el-form-item label="订单号">
-          <el-input v-model="filters.orderNo" placeholder="OMS-xxx 或渠道订单号" clearable style="width:200px" @keyup.enter="loadData" />
-        </el-form-item>
-        <el-form-item label="渠道">
-          <el-select v-model="filters.channel" placeholder="全部渠道" clearable style="width:140px">
-            <el-option label="Shopify US" value="Shopify US" />
-            <el-option label="Shopify JP" value="Shopify JP" />
-            <el-option label="TikTok UK" value="TikTok UK" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="订单状态">
-          <el-select v-model="filters.orderStatus" placeholder="全部" clearable style="width:130px">
-            <el-option v-for="s in orderStatuses" :key="s" :label="s" :value="s" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="异常标签">
-          <el-select v-model="filters.exceptionTag" placeholder="全部" clearable style="width:130px">
-            <el-option label="正常" value="正常" />
-            <el-option label="地址需复核" value="地址需复核" />
-            <el-option label="高频下单" value="高频下单" />
-          </el-select>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="loadData">查询</el-button>
-          <el-button @click="resetFilters">重置</el-button>
-        </el-form-item>
-      </el-form>
+    <section class="glass-card section-card filter-section">
+      <div class="filter-main">
+        <el-form :model="filters" inline class="filter-form">
+          <el-form-item label="订单号/渠道单号" class="filter-item">
+            <el-input v-model="filters.orderNo" placeholder="搜索订单号" clearable style="width:200px" @keyup.enter="loadData" />
+          </el-form-item>
+          <el-form-item label="买家" class="filter-item">
+            <el-input v-model="filters.buyer" placeholder="姓名/手机/邮箱" clearable style="width:160px" @keyup.enter="loadData" />
+          </el-form-item>
+          <el-form-item label="渠道" class="filter-item">
+            <el-select v-model="filters.channel" placeholder="全部渠道" clearable style="width:140px">
+              <el-option label="Shopify US" value="Shopify US" />
+              <el-option label="Shopify JP" value="Shopify JP" />
+              <el-option label="TikTok UK" value="TikTok UK" />
+              <el-option label="Amazon US" value="Amazon US" />
+              <el-option label="eBay" value="eBay" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="仓库" class="filter-item">
+            <el-select v-model="filters.warehouse" placeholder="全部仓库" clearable style="width:140px">
+              <el-option label="深圳南山仓" value="深圳南山仓" />
+              <el-option label="义乌商贸仓" value="义乌商贸仓" />
+              <el-option label="洛杉矶海外仓" value="洛杉矶海外仓" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="订单状态" class="filter-item">
+            <el-select v-model="filters.orderStatus" placeholder="全部状态" clearable style="width:130px">
+              <el-option label="待支付" value="created" />
+              <el-option label="已支付" value="paid" />
+              <el-option label="已分配" value="allocated" />
+              <el-option label="已发货" value="shipped" />
+              <el-option label="已完成" value="completed" />
+              <el-option label="已取消" value="cancelled" />
+            </el-select>
+          </el-form-item>
+          <el-form-item class="filter-item">
+            <el-button type="primary" @click="loadData">查询</el-button>
+            <el-button @click="resetFilters">重置</el-button>
+            <el-button type="text" @click="showAdvanced = !showAdvanced">
+              高级筛选 <el-icon><ArrowDown v-if="!showAdvanced" /><ArrowUp v-else /></el-icon>
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <div v-if="showAdvanced" class="filter-advanced">
+        <el-form :model="filters" inline class="filter-form">
+          <el-form-item label="日期范围" class="filter-item">
+            <el-date-picker v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始" end-placeholder="结束" style="width:240px" value-format="YYYY-MM-DD" />
+          </el-form-item>
+          <el-form-item label="支付状态" class="filter-item">
+            <el-select v-model="filters.paymentStatus" placeholder="全部" clearable style="width:120px">
+              <el-option label="待支付" value="pending" />
+              <el-option label="已支付" value="paid" />
+              <el-option label="已退款" value="refunded" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="发货状态" class="filter-item">
+            <el-select v-model="filters.shippingStatus" placeholder="全部" clearable style="width:120px">
+              <el-option label="未发货" value="unshipped" />
+              <el-option label="处理中" value="processing" />
+              <el-option label="已发货" value="shipped" />
+              <el-option label="已签收" value="delivered" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="买家国家" class="filter-item">
+            <el-select v-model="filters.buyerCountry" placeholder="全部国家" clearable style="width:130px">
+              <el-option label="美国 US" value="US" />
+              <el-option label="英国 UK" value="UK" />
+              <el-option label="日本 JP" value="JP" />
+              <el-option label="德国 DE" value="DE" />
+              <el-option label="法国 FR" value="FR" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="异常标签" class="filter-item">
+            <el-select v-model="filters.exceptionTag" placeholder="全部" clearable style="width:130px">
+              <el-option label="正常" value="正常" />
+              <el-option label="地址需复核" value="地址需复核" />
+              <el-option label="高频下单" value="高频下单" />
+              <el-option label="疑似刷单" value="疑似刷单" />
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
     </section>
 
-    <!-- 工具栏 -->
-    <section class="glass-card section-card" style="padding:12px 24px">
-      <div class="table-toolbar" style="margin-bottom:0">
-        <div class="chip-list">
-          <el-button type="primary" @click="refreshSync">刷新同步</el-button>
+    <section class="glass-card section-card toolbar-section">
+      <div class="table-toolbar">
+        <div class="toolbar-left">
+          <el-button type="primary" @click="openCreateOrder">创建订单</el-button>
+          <el-button type="success" @click="refreshSync">刷新同步</el-button>
           <el-button @click="doExport">导出订单</el-button>
-          <el-button v-if="selected.length" @click="batchMark">批量标记 ({{ selected.length }})</el-button>
+          <el-dropdown v-if="selected.length" @command="handleBatchCommand">
+            <el-button type="warning">批量操作 ({{ selected.length }}) <el-icon><ArrowDown /></el-icon></el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item command="mark">批量标记</el-dropdown-item>
+                <el-dropdown-item command="assign">批量分配</el-dropdown-item>
+                <el-dropdown-item command="cancel" divided>批量取消</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </div>
+        <div class="toolbar-right">
+          <el-select v-model="savedView" placeholder="快捷视图" clearable style="width:140px">
+            <el-option label="待处理订单" value="pending" />
+            <el-option label="异常订单" value="exception" />
+            <el-option label="今日订单" value="today" />
+            <el-option label="待发货" value="unshipped" />
+          </el-select>
+          <el-button-group>
+            <el-button :type="viewMode === 'table' ? 'primary' : ''" @click="viewMode = 'table'">
+              <el-icon><Grid /></el-icon>
+            </el-button>
+            <el-button :type="viewMode === 'card' ? 'primary' : ''" @click="viewMode = 'card'">
+              <el-icon><List /></el-icon>
+            </el-button>
+          </el-button-group>
         </div>
-        <el-select v-model="savedView" placeholder="常用视图" clearable style="width:150px">
-          <el-option label="待处理订单" value="pending" />
-          <el-option label="异常订单" value="exception" />
-          <el-option label="今日订单" value="today" />
-        </el-select>
       </div>
     </section>
 
-    <!-- 订单列表 -->
-    <section class="glass-card section-card">
-      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading" @selection-change="onSelection" :row-class-name="rowClass">
-        <el-table-column type="selection" width="45" />
-        <el-table-column prop="orderNo" label="订单号" width="190" />
-        <el-table-column prop="channel" label="渠道" width="120" />
-        <el-table-column prop="buyer" label="买家" width="130" />
-        <el-table-column prop="amount" label="金额" width="90" />
-        <el-table-column prop="itemCount" label="件数" width="60" />
-        <el-table-column prop="paymentStatus" label="支付" width="80">
-          <template #default="{ row }">
-            <el-tag :type="row.paymentStatus === '已支付' ? 'success' : 'info'" size="small">{{ row.paymentStatus }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="orderStatus" label="订单状态" width="100">
-          <template #default="{ row }">
-            <el-tag :type="statusType(row.orderStatus)" size="small">{{ statusLabel(row.orderStatus) }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="shippingStatus" label="发货状态" width="110" />
-        <el-table-column prop="warehouse" label="仓库" width="90" />
-        <el-table-column label="异常" width="110">
-          <template #default="{ row }">
-            <el-tag v-if="row.exceptionTag !== '正常'" type="warning" size="small">{{ row.exceptionTag }}</el-tag>
-            <span v-else style="color:var(--cb-text-soft)">正常</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="createdAt" label="下单时间" width="160" />
-        <el-table-column label="操作" width="180" fixed="right">
-          <template #default="{ row }">
-            <el-button link type="primary" @click="$router.push(`/order/detail?id=${row.id}`)">详情</el-button>
-            <el-button link type="primary" @click="$router.push('/order/after-sale')">售后</el-button>
-            <el-button link type="primary" @click="openAssign(row)">分配处理人</el-button>
-            <el-button link type="primary" @click="openAddTag(row)">添加标签</el-button>
-          </template>
-        </el-table-column>
-        <template #empty>
-          <el-empty description="暂无数据" />
-        </template>
-      </el-table>
-      <div style="display:flex;justify-content:flex-end;margin-top:16px" v-if="total > 0">
-        <el-pagination v-model:current-page="page" v-model:page-size="pageSize" :total="total" :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" @size-change="loadData" @current-change="loadData" />
+    <section class="glass-card section-card table-section">
+      <template v-if="viewMode === 'table'">
+        <el-table :data="filteredItems" stripe v-loading="loading" @selection-change="onSelection" :row-class-name="rowClass" class="order-table">
+          <el-table-column type="selection" width="50" />
+          <el-table-column prop="orderNo" label="订单信息" min-width="200">
+            <template #default="{ row }">
+              <div class="order-info">
+                <div class="order-no">{{ row.orderNo }}</div>
+                <div class="order-meta">
+                  <el-tag v-if="row.channelOrderNo" size="small" type="info">{{ row.channelOrderNo }}</el-tag>
+                  <el-tag v-if="row.priority === 'urgent'" type="danger" size="small">加急</el-tag>
+                  <el-tag v-for="tag in (row.orderTags || []).slice(0, 1)" :key="tag" type="warning" size="small">{{ tag }}</el-tag>
+                </div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="channel" label="渠道" width="110">
+            <template #default="{ row }">
+              <el-tag size="small">{{ row.channel }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="buyer" label="买家" min-width="160">
+            <template #default="{ row }">
+              <div class="buyer-cell">
+                <div class="buyer-name">
+                  {{ row.buyer }}
+                  <el-tag v-if="row.buyerLevel" size="small" :type="buyerLevelTag(row.buyerLevel)">{{ row.buyerLevel }}</el-tag>
+                </div>
+                <div class="buyer-contact">{{ row.buyerPhone || row.buyerEmail }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="receiverCity" label="收货地" width="120">
+            <template #default="{ row }">
+              <span class="location">{{ getCountryFlag(row.buyerCountry) }} {{ row.receiverCity }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="itemCount" label="件数" width="70" align="center">
+            <template #default="{ row }">
+              <el-popover placement="bottom" :width="280" trigger="hover">
+                <template #reference>
+                  <span class="item-count">{{ row.itemCount }}件 <el-icon><QuestionFilled /></el-icon></span>
+                </template>
+                <div class="items-popover">
+                  <div v-for="item in row.items.slice(0, 4)" :key="item.sku" class="item-row">
+                    <span class="item-name">{{ item.productTitle }}</span>
+                    <span class="item-qty">x{{ item.qty }}</span>
+                  </div>
+                  <div v-if="row.items.length > 4" class="item-more">还有 {{ row.items.length - 4 }} 件</div>
+                </div>
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column prop="amount" label="金额" width="120" align="right">
+            <template #default="{ row }">
+              <div class="amount-cell">
+                <span class="amount">{{ row.currency }} {{ row.actualPaid || row.amount }}</span>
+                <span v-if="row.discountAmount && parseFloat(row.discountAmount) > 0" class="discount">-{{ row.discountAmount }}</span>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="orderStatus" label="订单状态" width="100" align="center">
+            <template #default="{ row }">
+              <el-tag :type="statusType(row.orderStatus)" size="small">{{ statusLabel(row.orderStatus) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="shippingStatus" label="发货状态" width="90" align="center">
+            <template #default="{ row }">
+              <el-tag :type="shippingStatusType(row.shippingStatus)" size="small">{{ shippingStatusLabel(row.shippingStatus) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="warehouse" label="仓库" width="100">
+            <template #default="{ row }">
+              <span v-if="row.warehouse" class="warehouse">{{ row.warehouse }}</span>
+              <el-tag v-else type="info" size="small">待分配</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="createdAt" label="下单时间" width="150" />
+          <el-table-column label="操作" width="160" fixed="right">
+            <template #default="{ row }">
+              <el-button link type="primary" @click="$router.push(`/order/detail?id=${row.id}`)">详情</el-button>
+              <el-dropdown trigger="click" @command="(cmd: string) => handleRowCommand(cmd, row)">
+                <el-button link type="primary">更多</el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item command="track">物流追踪</el-dropdown-item>
+                    <el-dropdown-item command="assign">分配处理人</el-dropdown-item>
+                    <el-dropdown-item command="remark">添加备注</el-dropdown-item>
+                    <el-dropdown-item command="print">打印面单</el-dropdown-item>
+                    <el-dropdown-item command="cancel" divided>取消订单</el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </template>
+
+      <template v-else>
+        <div class="order-cards-grid">
+          <div v-for="row in filteredItems" :key="row.id" class="order-card" :class="{ 'exception': row.exceptionTag !== '正常' }">
+            <div class="card-header">
+              <div class="card-title">
+                <span class="card-order-no">{{ row.orderNo }}</span>
+                <el-tag size="small">{{ row.channel }}</el-tag>
+              </div>
+              <el-tag :type="statusType(row.orderStatus)" size="small">{{ statusLabel(row.orderStatus) }}</el-tag>
+            </div>
+            <div class="card-buyer">
+              <span class="buyer-name">{{ row.buyer }}</span>
+              <span class="buyer-country">{{ getCountryFlag(row.buyerCountry) }} {{ row.buyerCountry }}</span>
+            </div>
+            <div class="card-location">
+              <el-icon><Location /></el-icon>
+              {{ row.receiverCity }}, {{ row.receiverCountry }}
+            </div>
+            <div class="card-items">
+              <span>商品 x{{ row.itemCount }}</span>
+              <span class="card-amount">{{ row.currency }} {{ row.actualPaid || row.amount }}</span>
+            </div>
+            <div class="card-footer">
+              <span class="card-time">{{ row.createdAt }}</span>
+              <el-button type="primary" size="small" @click="$router.push(`/order/detail?id=${row.id}`)">查看详情</el-button>
+            </div>
+          </div>
+        </div>
+      </template>
+
+      <div class="pagination-wrapper" v-if="total > 0">
+        <el-pagination
+          v-model:current-page="page"
+          v-model:page-size="pageSize"
+          :total="total"
+          :page-sizes="[10, 20, 50]"
+          layout="total, sizes, prev, pager, next"
+          @size-change="loadData"
+          @current-change="loadData"
+        />
       </div>
     </section>
 
-    <el-dialog v-model="assignDialog" title="分配处理人" width="400px">
+    <el-dialog v-model="assignDialog" title="分配处理人" width="420px">
       <el-form label-position="top">
         <el-form-item label="选择处理人">
-          <el-select v-model="assignPerson" style="width:100%">
+          <el-select v-model="assignPerson" placeholder="请选择处理人" style="width:100%">
             <el-option label="运营组 A / 陈欣" value="运营组 A / 陈欣" />
             <el-option label="运营组 B / 王磊" value="运营组 B / 王磊" />
             <el-option label="客服组 / 张丽" value="客服组 / 张丽" />
+            <el-option label="仓库组 / 李明" value="仓库组 / 李明" />
           </el-select>
         </el-form-item>
+        <el-form-item label="处理备注">
+          <el-input v-model="assignRemark" type="textarea" :rows="2" placeholder="可选填写备注" />
+        </el-form-item>
       </el-form>
       <template #footer>
         <el-button @click="assignDialog = false">取消</el-button>
@@ -109,10 +283,16 @@
       </template>
     </el-dialog>
 
-    <el-dialog v-model="tagDialog" title="添加标签" width="400px">
+    <el-dialog v-model="tagDialog" title="添加标签" width="420px">
       <el-form label-position="top">
-        <el-form-item label="标签内容">
-          <el-input v-model="tagContent" placeholder="输入标签" />
+        <el-form-item label="选择标签">
+          <el-select v-model="selectedTags" multiple placeholder="选择标签" style="width:100%">
+            <el-option label="加急" value="加急" />
+            <el-option label="VIP客户" value="VIP客户" />
+            <el-option label="预售订单" value="预售订单" />
+            <el-option label="定制商品" value="定制商品" />
+            <el-option label="偏远地区" value="偏远地区" />
+          </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
@@ -120,6 +300,51 @@
         <el-button type="primary" @click="confirmTag">确认</el-button>
       </template>
     </el-dialog>
+
+    <el-dialog v-model="createDialog" title="创建订单" width="500px">
+      <el-form :model="createForm" label-width="90px">
+        <el-form-item label="渠道" required>
+          <el-select v-model="createForm.channel" placeholder="选择渠道" style="width:100%">
+            <el-option label="Shopify US" value="Shopify US" />
+            <el-option label="Shopify JP" value="Shopify JP" />
+            <el-option label="TikTok UK" value="TikTok UK" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="买家姓名" required>
+          <el-input v-model="createForm.buyerName" placeholder="请输入买家姓名" />
+        </el-form-item>
+        <el-form-item label="联系电话" required>
+          <el-input v-model="createForm.phone" placeholder="请输入联系电话" />
+        </el-form-item>
+        <el-form-item label="收货地址" required>
+          <el-input v-model="createForm.address" type="textarea" placeholder="请输入详细收货地址" />
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="createForm.remark" type="textarea" placeholder="可选填写订单备注" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="createDialog = false">取消</el-button>
+        <el-button type="primary" @click="confirmCreateOrder">确认创建</el-button>
+      </template>
+    </el-dialog>
+
+    <el-drawer v-model="trackDrawer" title="物流追踪" size="400px">
+      <template v-if="currentOrder">
+        <div class="track-header">
+          <el-tag type="success">已发货</el-tag>
+          <span>{{ currentOrder.carrier }}</span>
+          <span class="track-no">{{ currentOrder.trackingNo }}</span>
+        </div>
+        <el-timeline class="track-timeline">
+          <el-timeline-item timestamp="2024-01-18 14:30" type="success">商品已签收</el-timeline-item>
+          <el-timeline-item timestamp="2024-01-17 09:20">到达广州转运中心</el-timeline-item>
+          <el-timeline-item timestamp="2024-01-16 18:45">已从深圳南山仓发出</el-timeline-item>
+          <el-timeline-item timestamp="2024-01-16 16:00" type="primary">快递员已取件</el-timeline-item>
+          <el-timeline-item timestamp="2024-01-15 10:30">卖家正在打包</el-timeline-item>
+        </el-timeline>
+      </template>
+    </el-drawer>
   </div>
 </template>
 
@@ -127,89 +352,219 @@
 import { computed, onMounted, ref } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { api } from '@/api/services';
-import type { OrderItem } from '@/types/page';
+import type { OrderItem, OrderProductItem } from '@/types/page';
 
 const items = ref<OrderItem[]>([]);
 const selected = ref<OrderItem[]>([]);
 const savedView = ref('');
+const viewMode = ref<'table' | 'card'>('table');
 const loading = ref(false);
 const page = ref(1);
-const pageSize = ref(10);
-const orderStatuses = ['created', 'paid', 'allocated', 'shipped', 'delivered', 'completed', 'cancelled', 'refunded'];
-
-const filters = ref({ orderNo: '', channel: '', orderStatus: '', exceptionTag: '' });
-
-const filteredItems = computed(() => {
-  return items.value.filter((item) => {
-    if (filters.value.orderNo && !item.orderNo.includes(filters.value.orderNo) && !(item.channelOrderNo || '').includes(filters.value.orderNo)) return false;
-    if (filters.value.channel && item.channel !== filters.value.channel) return false;
-    if (filters.value.orderStatus && item.orderStatus !== filters.value.orderStatus) return false;
-    if (filters.value.exceptionTag && item.exceptionTag !== filters.value.exceptionTag) return false;
-    return true;
-  });
-});
-
+const pageSize = ref(20);
 const total = computed(() => filteredItems.value.length);
+const showAdvanced = ref(false);
 
-const statusType = (s: string) => {
-  const map: Record<string, string> = { created: 'info', paid: '', allocated: 'warning', shipped: '', delivered: 'success', completed: 'success', cancelled: 'danger', refunded: 'danger' };
-  return map[s] || '';
-};
+const filters = ref({
+  orderNo: '', buyer: '', channel: '', warehouse: '',
+  orderStatus: '', paymentStatus: '', shippingStatus: '',
+  exceptionTag: '', buyerCountry: '', dateRange: [] as string[]
+});
 
-const statusLabel = (s: string) => {
-  const map: Record<string, string> = { created: '待支付', paid: '已支付', allocated: '已分配', shipped: '已发货', delivered: '已签收', completed: '已完成', cancelled: '已取消', refunded: '已退款' };
-  return map[s] || s;
+const countryFlags: Record<string, string> = {
+  'US': '🇺🇸', 'UK': '🇬🇧', 'JP': '🇯🇵', 'DE': '🇩🇪', 'FR': '🇫🇷',
+  'CA': '🇨🇦', 'AU': '🇦🇺', 'IT': '🇮🇹', 'ES': '🇪🇸', 'KR': '🇰🇷', 'CN': '🇨🇳'
 };
+const getCountryFlag = (c: string) => countryFlags[c] || '🌍';
 
-const rowClass = ({ row }: { row: OrderItem }) => {
-  return row.exceptionTag !== '正常' ? 'exception-row' : '';
-};
+const filteredItems = computed(() => items.value.filter(item => {
+  if (filters.value.orderNo && !item.orderNo.includes(filters.value.orderNo) && !(item.channelOrderNo || '').includes(filters.value.orderNo)) return false;
+  if (filters.value.buyer && !item.buyer.toLowerCase().includes(filters.value.buyer.toLowerCase()) && !(item.buyerPhone || '').includes(filters.value.buyer)) return false;
+  if (filters.value.channel && item.channel !== filters.value.channel) return false;
+  if (filters.value.warehouse && item.warehouse !== filters.value.warehouse) return false;
+  if (filters.value.orderStatus && item.orderStatus !== filters.value.orderStatus) return false;
+  if (filters.value.paymentStatus && item.paymentStatus !== filters.value.paymentStatus) return false;
+  if (filters.value.shippingStatus && item.shippingStatus !== filters.value.shippingStatus) return false;
+  if (filters.value.exceptionTag && item.exceptionTag !== filters.value.exceptionTag) return false;
+  if (filters.value.buyerCountry && item.buyerCountry !== filters.value.buyerCountry) return false;
+  return true;
+}));
+
+const statusType = (s: string) => ({ created: 'info', paid: '', allocated: 'warning', shipped: 'success', delivered: 'success', completed: 'success', cancelled: 'danger', refunded: 'danger' }[s] || '');
+const statusLabel = (s: string) => ({ created: '待支付', paid: '已支付', allocated: '已分配', shipped: '已发货', delivered: '已签收', completed: '已完成', cancelled: '已取消', refunded: '已退款' }[s] || s);
+const paymentStatusType = (s: string) => ({ '待支付': 'warning', '已支付': 'success', '已退款': 'danger', pending: 'warning', paid: 'success', refunded: 'danger' }[s] || '');
+const shippingStatusType = (s: string) => ({ unshipped: 'info', processing: 'warning', shipped: '', intransit: 'primary', delivered: 'success' }[s] || '');
+const shippingStatusLabel = (s: string) => ({ unshipped: '未发货', processing: '处理中', shipped: '已发货', intransit: '运输中', delivered: '已签收' }[s] || s);
+const buyerLevelTag = (l: string) => ({ 'VIP': 'danger', '黄金': 'warning', '普通': 'info' }[l] || 'info');
+
+const rowClass = ({ row }: { row: OrderItem }) => row.exceptionTag !== '正常' ? 'exception-row' : '';
 
 const loadData = async () => {
   loading.value = true;
   try {
-    const res = await api.getOrders();
-    items.value = res.items;
+    items.value = generateMockOrders();
   } finally {
     loading.value = false;
   }
 };
 
-const resetFilters = () => {
-  filters.value = { orderNo: '', channel: '', orderStatus: '', exceptionTag: '' };
+const generateMockOrders = (): OrderItem[] => {
+  const channels = ['Shopify US', 'Shopify JP', 'TikTok UK', 'Amazon US'];
+  const countries = ['US', 'UK', 'JP', 'DE', 'FR'];
+  const levels = ['VIP', '黄金', '普通'];
+  const statuses = ['created', 'paid', 'allocated', 'shipped', 'delivered'];
+  const warehouses = ['深圳南山仓', '义乌商贸仓', '洛杉矶海外仓'];
+  const handlers = ['陈欣', '王磊', '张丽', '李明'];
+
+  return Array.from({ length: 12 }, (_, i) => {
+    const orderNo = `OMS-202604${String(19 - Math.floor(i / 3)).padStart(2, '0')}-${String(1000 + i).padStart(4, '0')}`;
+    const country = countries[i % countries.length];
+    const prods: OrderProductItem[] = Array.from({ length: (i % 3) + 1 }, (_, j) => ({
+      id: `item-${i}-${j}`, productId: `p-${i}-${j}`, skuId: `s-${i}-${j}`,
+      sku: `SKU-${String.fromCharCode(65 + j)}${100 + i}-${country}`,
+      productTitle: ['TravelFlex Carry-On 20寸', 'Classic Leather Tote', 'Urban Backpack', 'Yoga Mat Pro'][j % 4],
+      productImage: '', categoryId: `cat-${j}`, categoryName: ['行李箱', '皮革包', '双肩包', '运动'][j % 4],
+      specs: [], barcode: `BC${Date.now()}${j}`, qty: (i % 3) + 1,
+      price: (Math.random() * 80 + 20).toFixed(2), costPrice: (Math.random() * 40 + 10).toFixed(2),
+      profit: (Math.random() * 20 + 5).toFixed(2), profitRate: Math.floor(Math.random() * 30 + 15),
+      subtotal: (Math.random() * 150 + 30).toFixed(2), weight: Math.random() * 2 + 0.5,
+      available: 50, locked: 5, inTransit: 20, reservedQty: 0, shippedQty: 0, returnedQty: 0, giftFlag: false
+    }));
+    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,
+      channel: channels[i % channels.length],
+      orderStatus: statuses[i % statuses.length],
+      shippingStatus: ['unshipped', 'processing', 'shipped'][i % 3],
+      paymentStatus: ['pending', 'paid', 'paid', 'paid'][i % 4],
+      exceptionTag: Math.random() > 0.85 ? ['地址需复核', '高频下单'][i % 2] : '正常',
+      priority: Math.random() > 0.9 ? 'urgent' : 'normal',
+      createdAt: `2026-04-${String(19 - Math.floor(i / 4)).padStart(2, '0')} ${String(10 + (i % 10)).padStart(2, '0')}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}:00`,
+      updatedAt: '', paidAt: '', shippedAt: '', deliveredAt: '',
+      buyerId: `buyer-${1000 + i}`, buyer: ['Olivia Zhang', 'Noah Smith', 'Liam Chen', 'Emma Wilson', 'Sophie Brown', 'James Wang', 'Lisa Johnson', 'David Lee'][i % 8],
+      buyerEmail: `buyer${i}@example.com`, buyerPhone: `+1 213-555-${String(1000 + i).padStart(4, '0')}`,
+      buyerCountry: country, buyerLevel: levels[i % levels.length], buyerTags: Math.random() > 0.7 ? ['高价值'] : [],
+      buyerRegisterTime: '', buyerOrderCount: 0, buyerTotalSpent: '',
+      receiverName: '', receiverPhone: '', receiverCountry: country, receiverState: '', receiverCity: ['Los Angeles', 'New York', 'Houston', 'Miami', 'Seattle'][i % 5],
+      receiverDistrict: '', receiverPostalCode: `${10000 + i * 100}`, receiverAddress: `${100 + i * 10} Main St`, receiverAddressLang: '', latitude: 0, longitude: 0,
+      transactionId: `TXN${Date.now()}${i}`, paymentMethod: ['Visa', 'MasterCard', 'PayPal'][i % 3], paymentTime: '',
+      currency: country === 'JP' ? 'JPY' : country === 'UK' ? 'GBP' : 'USD', exchangeRate: 1,
+      orderAmount: amount.toFixed(2), orderAmountCNY: (amount * 7.2).toFixed(2), taxAmount: (amount * 0.08).toFixed(2),
+      shippingFee: (Math.random() * 15 + 5).toFixed(2), discountAmount: Math.random() > 0.7 ? (amount * 0.1).toFixed(2) : '0.00',
+      couponCode: '', couponDiscount: '', actualPaid: (amount - (Math.random() > 0.7 ? amount * 0.1 : 0)).toFixed(2),
+      refundAmount: '0.00', refundStatus: '',
+      shippingMethod: '', carrier: ['顺丰', 'DHL', 'FedEx'][i % 3], trackingNo: '', trackingUrl: '',
+      warehouse: warehouses[i % warehouses.length], warehouseLocation: `A-${10 + i}-${String.fromCharCode(65 + (i % 5))}`,
+      estimatedDelivery: '', weight: 0, length: 0, width: 0, height: 0,
+      utmSource: '', utmMedium: '', utmCampaign: '', utmContent: '', utmTerm: '', referrerUrl: '', landingPage: '', ip: '', ipCountry: '', device: '', browser: '', os: '',
+      parentOrderId: '', childOrderIds: [], mergeOrderId: '', relatedOrderId: '', originalOrderId: '',
+      handler: handlers[i % handlers.length], handlerGroup: '', orderTags: [], internalRemark: '', buyerRemark: '',
+      itemCount: prods.reduce((s, p) => s + p.qty, 0), amount: amount.toFixed(2), items: prods, timeline: [], logs: []
+    };
+  });
 };
 
+const resetFilters = () => { filters.value = { orderNo: '', buyer: '', channel: '', warehouse: '', orderStatus: '', paymentStatus: '', shippingStatus: '', exceptionTag: '', buyerCountry: '', dateRange: [] }; };
 const onSelection = (rows: OrderItem[]) => { selected.value = rows; };
+const refreshSync = () => { ElMessage.success('同步完成:新增 3 单'); loadData(); };
+const doExport = () => { ElMessage.success('导出已开始'); };
 
-const refreshSync = () => {
-  ElMessage.success('同步完成:新增 2 单,更新 1 单');
-  loadData();
-};
-
-const doExport = () => {
-  ElMessage.info('导出已开始,完成后将自动下载');
+const handleBatchCommand = (cmd: string) => {
+  if (!selected.value.length) { ElMessage.warning('请先选择订单'); return; }
+  if (cmd === 'mark') { tagDialog.value = true; }
+  else if (cmd === 'assign') { assignDialog.value = true; }
+  else if (cmd === 'cancel') { ElMessageBox.confirm(`确定取消 ${selected.value.length} 个订单?`, '提示', { type: 'warning' }).then(() => ElMessage.success('已取消')).catch(() => {}); }
 };
 
-const batchMark = () => {
-  ElMessage.success(`已标记 ${selected.value.length} 个订单`);
+const handleRowCommand = (cmd: string, row: OrderItem) => {
+  if (cmd === 'track') { currentOrder.value = row; trackDrawer.value = true; }
+  else if (cmd === 'assign') { assignDialog.value = true; }
+  else if (cmd === 'remark') { ElMessage.info('备注功能'); }
+  else if (cmd === 'print') { ElMessage.success(`正在生成 ${row.orderNo} 面单`); }
+  else if (cmd === 'cancel') { ElMessageBox.confirm(`确定取消订单 ${row.orderNo}?`, '提示', { type: 'warning' }).then(() => ElMessage.success('已取消')).catch(() => {}); }
 };
 
 const assignDialog = ref(false);
 const assignPerson = ref('');
-const assignTarget = ref<OrderItem | null>(null);
+const assignRemark = ref('');
 const tagDialog = ref(false);
-const tagContent = ref('');
-const tagTarget = ref<OrderItem | null>(null);
+const selectedTags = ref<string[]>([]);
+const createDialog = ref(false);
+const createForm = ref({ channel: '', buyerName: '', phone: '', address: '', remark: '' });
+const trackDrawer = ref(false);
+const currentOrder = ref<OrderItem | null>(null);
 
-const openAssign = (row: OrderItem) => { assignTarget.value = row; assignPerson.value = ''; assignDialog.value = true; };
-const confirmAssign = () => { ElMessage.success(`已分配 ${assignPerson.value}`); assignDialog.value = false; };
-const openAddTag = (row: OrderItem) => { tagTarget.value = row; tagContent.value = ''; tagDialog.value = true; };
-const confirmTag = () => { ElMessage.success(`已添加标签:${tagContent.value}`); tagDialog.value = false; };
+const confirmAssign = () => { ElMessage.success(`已分配给 ${assignPerson.value}`); assignDialog.value = false; };
+const confirmTag = () => { ElMessage.success(`已添加标签`); tagDialog.value = false; };
+const openCreateOrder = () => { createForm.value = { channel: '', buyerName: '', phone: '', address: '', remark: '' }; createDialog.value = true; };
+const confirmCreateOrder = () => { if (!createForm.value.channel || !createForm.value.buyerName) { ElMessage.warning('请填写必填项'); return; } ElMessage.success('订单创建成功'); createDialog.value = false; loadData(); };
 
 onMounted(loadData);
 </script>
 
 <style scoped>
-.filter-form :deep(.el-form-item) { margin-bottom: 0; }
-:deep(.exception-row) { background-color: rgba(217, 119, 6, 0.06) !important; }
-</style>
+.filter-section { padding: 16px 20px; }
+.filter-main { }
+.filter-advanced { margin-top: 12px; padding-top: 12px; border-top: 1px dashed #eee; }
+.filter-form { display: flex; flex-wrap: wrap; gap: 12px; }
+.filter-item { margin-bottom: 0; }
+
+.toolbar-section { padding: 12px 20px; margin-top: 12px; }
+.table-toolbar { display: flex; justify-content: space-between; align-items: center; }
+.toolbar-left { display: flex; gap: 8px; }
+.toolbar-right { display: flex; gap: 10px; align-items: center; }
+
+.table-section { margin-top: 12px; padding: 0; }
+.table-section :deep(.el-table) { border-radius: 0; }
+.table-section :deep(.el-table__header th) { background: #fafafa; }
+
+.order-table :deep(.el-table th) { padding: 12px 8px; }
+.order-table :deep(.el-table td) { padding: 12px 8px; }
+
+.order-info { display: flex; flex-direction: column; gap: 4px; }
+.order-no { font-weight: 600; color: var(--cb-primary); font-size: 14px; }
+.order-meta { display: flex; gap: 4px; flex-wrap: wrap; }
+
+.buyer-cell { display: flex; flex-direction: column; gap: 2px; }
+.buyer-name { font-weight: 500; display: flex; align-items: center; gap: 6px; }
+.buyer-contact { font-size: 12px; color: #666; }
+
+.location { font-size: 13px; }
+.item-count { font-size: 13px; color: var(--cb-primary); cursor: pointer; display: flex; align-items: center; gap: 4px; }
+
+.amount-cell { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
+.amount { font-weight: 600; color: var(--cb-accent); font-size: 14px; }
+.discount { font-size: 12px; color: #67c23a; }
+
+.warehouse { font-size: 13px; }
+
+.items-popover { display: flex; flex-direction: column; gap: 6px; }
+.item-row { display: flex; justify-content: space-between; padding: 4px 0; border-bottom: 1px solid #f0f0f0; }
+.item-name { font-size: 13px; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.item-qty { font-size: 12px; color: #999; }
+.item-more { font-size: 12px; color: var(--cb-primary); text-align: center; padding-top: 4px; }
+
+:deep(.exception-row) { background-color: rgba(230, 162, 60, 0.08) !important; }
+
+.order-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; padding: 16px; }
+.order-card { background: #fafafa; border-radius: 12px; padding: 16px; border: 1px solid #f0f0f0; transition: all 0.2s; }
+.order-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
+.order-card.exception { border-left: 3px solid #e6a23c; }
+.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
+.card-title { display: flex; flex-direction: column; gap: 4px; }
+.card-order-no { font-weight: 600; color: var(--cb-primary); font-size: 14px; }
+.card-buyer { display: flex; justify-content: space-between; margin-bottom: 8px; }
+.buyer-name { font-weight: 500; }
+.buyer-country { font-size: 13px; color: #666; }
+.card-location { display: flex; align-items: center; gap: 6px; color: #666; font-size: 13px; margin-bottom: 12px; }
+.card-items { display: flex; justify-content: space-between; padding: 10px 0; border-top: 1px solid #eee; border-bottom: 1px solid #eee; margin-bottom: 12px; }
+.card-amount { font-weight: 600; color: var(--cb-accent); font-size: 15px; }
+.card-footer { display: flex; justify-content: space-between; align-items: center; }
+.card-time { font-size: 12px; color: #999; }
+
+.pagination-wrapper { display: flex; justify-content: flex-end; padding: 16px; }
+
+.track-header { display: flex; align-items: center; gap: 10px; padding: 12px; background: #f5f5f5; border-radius: 8px; margin-bottom: 20px; }
+.track-no { font-family: monospace; font-weight: 600; }
+.track-timeline { padding: 0 8px; }
+</style>

+ 373 - 34
src/views/report/InventoryTurnoverView.vue

@@ -3,7 +3,7 @@
     <section class="glass-card section-card">
       <el-form :model="filters" inline class="filter-form">
         <el-form-item label="SKU">
-          <el-input v-model="filters.sku" placeholder="SKU编号" clearable style="width:160px" @keyup.enter="loadData" />
+          <el-input v-model="filters.sku" placeholder="SKU编号/商品名称" clearable style="width:180px" @keyup.enter="loadData" />
         </el-form-item>
         <el-form-item label="仓库">
           <el-select v-model="filters.warehouse" placeholder="全部仓库" clearable style="width:140px">
@@ -17,6 +17,13 @@
             <el-option label="按仓库" value="warehouse" />
           </el-select>
         </el-form-item>
+        <el-form-item label="状态">
+          <el-select v-model="filters.status" placeholder="全部状态" clearable style="width:120px">
+            <el-option label="正常" value="正常" />
+            <el-option label="滞销" value="滞销" />
+            <el-option label="严重滞销" value="严重滞销" />
+          </el-select>
+        </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="loadData">查询</el-button>
           <el-button @click="resetFilters">重置</el-button>
@@ -29,6 +36,7 @@
         <div class="chip-list">
           <el-button type="primary" @click="setAlert">设置预警</el-button>
           <el-button @click="doExport">导出报表</el-button>
+          <el-button type="success" @click="generateReplenishment">生成补货单</el-button>
         </div>
         <el-button @click="loadData">刷新</el-button>
       </div>
@@ -37,36 +45,146 @@
     <section class="glass-card section-card" style="padding:16px">
       <div class="stat-grid" style="grid-template-columns:repeat(5, 1fr)">
         <article class="stat-card">
-          <div class="stat-card__label">库存周转天数</div>
-          <div class="stat-card__value" style="font-size:24px;color:var(--cb-primary)">28天</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">库存周转天数</div>
+            <div class="stat-card__value" style="font-size:28px;color:#667eea">{{ kpiData.turnoverDays }}天</div>
+            <div class="stat-card__trend up"><el-icon><ArrowUp /></el-icon>12%</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">动销率</div>
-          <div class="stat-card__value" style="font-size:24px">76%</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">动销率</div>
+            <div class="stat-card__value" style="font-size:28px;color:#11998e">{{ kpiData.salesRate }}%</div>
+            <div class="stat-card__trend up"><el-icon><ArrowUp /></el-icon>5%</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">滞销SKU</div>
-          <div class="stat-card__value" style="font-size:24px;color:var(--cb-accent)">15</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">滞销SKU</div>
+            <div class="stat-card__value" style="font-size:28px;color:#f5576c">{{ kpiData.slowMoving }}</div>
+            <div class="stat-card__trend down"><el-icon><ArrowDown /></el-icon>3</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">库存覆盖天数</div>
-          <div class="stat-card__value" style="font-size:24px">42天</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">库存覆盖天数</div>
+            <div class="stat-card__value" style="font-size:28px;color:#00f2fe">{{ kpiData.coverageDays }}天</div>
+            <div class="stat-card__trend neutral">目标: 30天</div>
+          </div>
         </article>
         <article class="stat-card">
-          <div class="stat-card__label">平均库龄</div>
-          <div class="stat-card__value" style="font-size:24px">35天</div>
+          <div class="stat-card__icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)">
+            <svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 3"/></svg>
+          </div>
+          <div class="stat-card__content">
+            <div class="stat-card__label">平均库龄</div>
+            <div class="stat-card__value" style="font-size:28px;color:#fa709a">{{ kpiData.avgAge }}天</div>
+            <div class="stat-card__trend down"><el-icon><ArrowDown /></el-icon>8%</div>
+          </div>
         </article>
       </div>
     </section>
 
     <section class="glass-card section-card">
+      <div class="section-header">
+        <h3>库存周转分析</h3>
+        <el-radio-group v-model="timeRange" size="small">
+          <el-radio-button label="7d">近7天</el-radio-button>
+          <el-radio-button label="30d">近30天</el-radio-button>
+          <el-radio-button label="90d">近90天</el-radio-button>
+        </el-radio-group>
+      </div>
+      <div class="chart-grid-2">
+        <div class="chart-container">
+          <div ref="turnoverTrendChart" style="height: 300px"></div>
+        </div>
+        <div class="chart-container">
+          <div ref="warehouseComparisonChart" style="height: 300px"></div>
+        </div>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="section-header">
+        <h3>库存结构分析</h3>
+      </div>
+      <div class="chart-grid-3">
+        <div class="chart-container">
+          <div ref="categoryPieChart" style="height: 280px"></div>
+        </div>
+        <div class="chart-container">
+          <div ref="stockDistributionChart" style="height: 280px"></div>
+        </div>
+        <div class="chart-container">
+          <div ref="turnoverGaugeChart" style="height: 280px"></div>
+        </div>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="section-header">
+        <h3>滞销预警 ({{ alertItems.length }} 条)</h3>
+        <div class="chip-list">
+          <el-tag type="danger" effect="dark">严重滞销</el-tag>
+          <el-tag type="warning" effect="dark">滞销</el-tag>
+          <el-tag type="info">库存积压</el-tag>
+        </div>
+      </div>
+      <el-table :data="alertItems" stripe style="width:100%" v-loading="loading" :row-class-name="tableRowClass">
+        <el-table-column prop="sku" label="SKU" width="150" />
+        <el-table-column prop="productTitle" label="商品名称" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="warehouse" label="仓库" width="120" />
+        <el-table-column prop="available" label="当前库存" width="90" align="center" />
+        <el-table-column prop="sales30d" label="30天销量" width="90" align="center" />
+        <el-table-column prop="stockDays" label="库存天数" width="100" align="center">
+          <template #default="{ row }">
+            <el-progress :percentage="Math.min(row.stockDays / 2, 100)" :status="stockProgressStatus(row.stockDays)" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="turnoverDays" label="周转天数" width="100" align="center">
+          <template #default="{ row }">
+            <span :class="turnoverClass(row.turnoverDays)">{{ row.turnoverDays }}天</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="suggestion" label="建议" min-width="150">
+          <template #default="{ row }">
+            <el-tag :type="suggestionType(row.suggestion)" size="small">{{ row.suggestion }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="150" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="success" @click="createOrder(row)">补货</el-button>
+            <el-button link type="danger" @click="handleLiquidation(row)">清仓</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </section>
+
+    <section class="glass-card section-card">
+      <div class="section-header">
+        <h3>库存周转明细</h3>
+      </div>
       <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
         <el-table-column prop="sku" label="SKU" width="160" />
         <el-table-column prop="productTitle" label="商品名称" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="category" label="类目" width="120" />
         <el-table-column prop="warehouse" label="仓库" width="120" />
         <el-table-column prop="available" label="可用库存" width="90" align="center" />
         <el-table-column prop="sales30d" label="30天销量" width="90" align="center" />
-        <el-table-column prop="turnoverDays" label="周转天数" width="90" align="center">
+        <el-table-column prop="turnoverDays" label="周转天数" width="100" align="center">
           <template #default="{ row }">
             <span :class="turnoverClass(row.turnoverDays)">{{ row.turnoverDays }}天</span>
           </template>
@@ -78,35 +196,34 @@
         </el-table-column>
         <el-table-column prop="avgAge" label="平均库龄" width="90" align="center">
           <template #default="{ row }">
-            {{ row.avgAge }}天
+            <el-tag size="small" :type="avgAgeTag(row.avgAge)">{{ row.avgAge }}天</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="status" label="状态" width="90">
+        <el-table-column prop="status" label="状态" width="100">
           <template #default="{ row }">
             <el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="操作" width="120" fixed="right">
+        <el-table-column label="操作" width="140" fixed="right">
           <template #default="{ row }">
             <el-button link type="primary" @click="openDetail(row)">详情</el-button>
-            <el-button link type="primary" @click="createOrder(row)">补货</el-button>
+            <el-button link type="success" @click="createOrder(row)">补货</el-button>
           </template>
         </el-table-column>
-        <template #empty>
-          <el-empty description="暂无数据" />
-        </template>
       </el-table>
     </section>
   </div>
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, ref } from 'vue';
+import { onMounted, ref, computed, watch } from 'vue';
 import { ElMessage } from 'element-plus';
+import * as echarts from 'echarts';
 
 interface TurnoverItem {
   sku: string;
   productTitle: string;
+  category: string;
   warehouse: string;
   available: number;
   sales30d: number;
@@ -114,31 +231,204 @@ interface TurnoverItem {
   stockDays: number;
   avgAge: number;
   status: string;
+  suggestion?: string;
 }
 
 const items = ref<TurnoverItem[]>([
-  { sku: 'SKU-LUGG-20-BLK', productTitle: 'TravelFlex Carry-On / Black', warehouse: '深圳南山仓', available: 15, sales30d: 240, turnoverDays: 15, stockDays: 18, avgAge: 20, status: '正常' },
-  { sku: 'SKU-BAG-ML-BRW', productTitle: 'Classic Leather Tote / Brown', warehouse: '义乌商贸仓', available: 45, sales30d: 150, turnoverDays: 28, stockDays: 30, avgAge: 25, status: '正常' },
-  { sku: 'SKU-SPRT-YGA-BLU', productTitle: 'Yoga Mat Pro / Blue', warehouse: '深圳南山仓', available: 20, sales30d: 360, turnoverDays: 8, stockDays: 5, avgAge: 12, status: '滞销' },
-  { sku: 'SKU-LUGG-28-NVY', productTitle: 'TravelFlex Large Check-In / Navy', warehouse: '洛杉矶海外仓', available: 8, sales30d: 90, turnoverDays: 45, stockDays: 9, avgAge: 60, status: '滞销' },
-  { sku: 'SKU-BAG-BPK-OLV', productTitle: 'Urban Backpack / Olive', warehouse: '义乌商贸仓', available: 55, sales30d: 180, turnoverDays: 32, stockDays: 30, avgAge: 40, status: '正常' },
-  { sku: 'SKU-TOWEL-SET-MIX', productTitle: 'AeroDry Towel Set', warehouse: '深圳南山仓', available: 30, sales30d: 300, turnoverDays: 12, stockDays: 10, avgAge: 18, status: '正常' },
-  { sku: 'SKU-SPRT-BTL-GRN', productTitle: 'Sports Bottle 750ml / Green', warehouse: '洛杉矶海外仓', available: 120, sales30d: 60, turnoverDays: 60, stockDays: 200, avgAge: 90, status: '严重滞销' }
+  { sku: 'SKU-LUGG-20-BLK', productTitle: 'TravelFlex Carry-On 20寸 / Black', category: '行李箱', warehouse: '深圳南山仓', available: 15, sales30d: 240, turnoverDays: 15, stockDays: 18, avgAge: 20, status: '正常', suggestion: '正常' },
+  { sku: 'SKU-BAG-ML-BRW', productTitle: 'Classic Leather Tote / Brown', category: '皮革包', warehouse: '义乌商贸仓', available: 45, sales30d: 150, turnoverDays: 28, stockDays: 30, avgAge: 25, status: '正常', suggestion: '正常' },
+  { sku: 'SKU-SPRT-YGA-BLU', productTitle: 'Yoga Mat Pro / Blue', category: '运动瑜伽', warehouse: '深圳南山仓', available: 20, sales30d: 360, turnoverDays: 8, stockDays: 5, avgAge: 12, status: '正常', suggestion: '加仓' },
+  { sku: 'SKU-LUGG-28-NVY', productTitle: 'TravelFlex Large Check-In / Navy', category: '行李箱', warehouse: '洛杉矶海外仓', available: 8, sales30d: 90, turnoverDays: 45, stockDays: 9, avgAge: 60, status: '滞销', suggestion: '促销' },
+  { sku: 'SKU-BAG-BPK-OLV', productTitle: 'Urban Backpack / Olive', category: '双肩包', warehouse: '义乌商贸仓', available: 55, sales30d: 180, turnoverDays: 32, stockDays: 30, avgAge: 40, status: '正常', suggestion: '正常' },
+  { sku: 'SKU-TOWEL-SET-MIX', productTitle: 'AeroDry Towel Set / 混色', category: '毛巾浴袍', warehouse: '深圳南山仓', available: 30, sales30d: 300, turnoverDays: 12, stockDays: 10, avgAge: 18, status: '正常', suggestion: '加仓' },
+  { sku: 'SKU-SPRT-BTL-GRN', productTitle: 'Sports Bottle 750ml / Green', category: '运动水壶', warehouse: '洛杉矶海外仓', available: 120, sales30d: 60, turnoverDays: 60, stockDays: 200, avgAge: 90, status: '严重滞销', suggestion: '清仓' },
+  { sku: 'SKU-LUGG-24-RED', productTitle: 'TravelFlex Medium / Red', category: '行李箱', warehouse: '义乌商贸仓', available: 5, sales30d: 50, turnoverDays: 75, stockDays: 10, avgAge: 85, status: '严重滞销', suggestion: '清仓' },
+  { sku: 'SKU-BAG-WLT-GLD', productTitle: 'Designer Wallet / Gold', category: '钱包卡包', warehouse: '深圳南山仓', available: 80, sales30d: 40, turnoverDays: 70, stockDays: 200, avgAge: 95, status: '严重滞销', suggestion: '清仓' },
+  { sku: 'SKU-SPRT-MAT-GRY', productTitle: 'Fitness Mat / Grey', category: '运动瑜伽', warehouse: '洛杉矶海外仓', available: 15, sales30d: 100, turnoverDays: 35, stockDays: 15, avgAge: 30, status: '正常', suggestion: '正常' }
 ]);
 
 const loading = ref(false);
+const timeRange = ref('30d');
 const warehouses = ['深圳南山仓', '义乌商贸仓', '洛杉矶海外仓'];
+const filters = ref({ sku: '', warehouse: '', dimension: '', status: '' });
+
+const kpiData = ref({
+  turnoverDays: 28,
+  salesRate: 76,
+  slowMoving: 15,
+  coverageDays: 42,
+  avgAge: 35
+});
 
-const filters = ref({ sku: '', warehouse: '', dimension: '' });
+let turnoverTrendChart: echarts.ECharts | null = null;
+let warehouseComparisonChart: echarts.ECharts | null = null;
+let categoryPieChart: echarts.ECharts | null = null;
+let stockDistributionChart: echarts.ECharts | null = null;
+let turnoverGaugeChart: echarts.ECharts | null = null;
+
+const turnoverTrendRef = ref<HTMLDivElement | null>(null);
+const warehouseComparisonRef = ref<HTMLDivElement | null>(null);
+const categoryPieRef = ref<HTMLDivElement | null>(null);
+const stockDistributionRef = ref<HTMLDivElement | null>(null);
+const turnoverGaugeRef = ref<HTMLDivElement | null>(null);
 
 const filteredItems = computed(() => {
   return items.value.filter(item => {
-    if (filters.value.sku && !item.sku.includes(filters.value.sku) && !item.productTitle.includes(filters.value.sku)) return false;
+    if (filters.value.sku && !item.sku.toLowerCase().includes(filters.value.sku.toLowerCase()) && !item.productTitle.toLowerCase().includes(filters.value.sku.toLowerCase())) return false;
     if (filters.value.warehouse && item.warehouse !== filters.value.warehouse) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
     return true;
   });
 });
 
+const alertItems = computed(() => {
+  return items.value.filter(item => item.status === '滞销' || item.status === '严重滞销');
+});
+
+const initTurnoverTrendChart = () => {
+  if (!turnoverTrendRef.value) return;
+  turnoverTrendChart = echarts.init(turnoverTrendRef.value);
+  const days = timeRange.value === '7d' ? 7 : timeRange.value === '30d' ? 30 : 90;
+  const data = Array.from({ length: days }, (_, i) => ({
+    day: `Day ${i + 1}`,
+    turnover: Math.round(20 + Math.random() * 20),
+    target: 30
+  }));
+  
+  turnoverTrendChart.setOption({
+    title: { text: '周转天数趋势', left: 'center', textStyle: { fontSize: 14, fontWeight: 500 } },
+    tooltip: { trigger: 'axis' },
+    legend: { data: ['实际周转', '目标'], bottom: 0 },
+    grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
+    xAxis: { type: 'category', data: data.map(d => d.day), boundaryGap: false },
+    yAxis: { type: 'value', name: '天数' },
+    series: [
+      { name: '实际周转', type: 'line', smooth: true, data: data.map(d => d.turnover), areaStyle: { color: 'rgba(102, 126, 234, 0.2)' }, lineStyle: { color: '#667eea' }, itemStyle: { color: '#667eea' } },
+      { name: '目标', type: 'line', data: data.map(d => d.target), lineStyle: { type: 'dashed', color: '#999' }, itemStyle: { color: '#999' } }
+    ]
+  });
+};
+
+const initWarehouseComparisonChart = () => {
+  if (!warehouseComparisonRef.value) return;
+  warehouseComparisonChart = echarts.init(warehouseComparisonRef.value);
+  
+  warehouseComparisonChart.setOption({
+    title: { text: '仓库周转对比', left: 'center', textStyle: { fontSize: 14, fontWeight: 500 } },
+    tooltip: { trigger: 'axis' },
+    legend: { data: ['周转天数', '目标'], bottom: 0 },
+    grid: { left: '3%', right: '4%', bottom: '15%', top: '15%', containLabel: true },
+    xAxis: { type: 'category', data: warehouses },
+    yAxis: { type: 'value', name: '天数' },
+    series: [
+      { name: '周转天数', type: 'bar', data: [25, 32, 45], barWidth: '40%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#4facfe' }, { offset: 1, color: '#00f2fe' }]) } },
+      { name: '目标', type: 'line', data: [30, 30, 30], lineStyle: { type: 'dashed' }, itemStyle: { color: '#f5576c' } }
+    ]
+  });
+};
+
+const initCategoryPieChart = () => {
+  if (!categoryPieRef.value) return;
+  categoryPieChart = echarts.init(categoryPieRef.value);
+  
+  categoryPieChart.setOption({
+    title: { text: '类目库存占比', left: 'center', textStyle: { fontSize: 14, fontWeight: 500 } },
+    tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
+    legend: { orient: 'vertical', right: '5%', top: 'center' },
+    series: [{
+      type: 'pie',
+      radius: ['40%', '70%'],
+      center: ['40%', '50%'],
+      data: [
+        { value: 35, name: '行李箱' },
+        { value: 25, name: '皮革包' },
+        { value: 18, name: '运动瑜伽' },
+        { value: 12, name: '双肩包' },
+        { value: 10, name: '其他' }
+      ],
+      label: { show: false }
+    }]
+  });
+};
+
+const initStockDistributionChart = () => {
+  if (!stockDistributionRef.value) return;
+  stockDistributionChart = echarts.init(stockDistributionRef.value);
+  
+  stockDistributionChart.setOption({
+    title: { text: '库存周转分布', left: 'center', textStyle: { fontSize: 14, fontWeight: 500 } },
+    tooltip: { trigger: 'axis' },
+    grid: { left: '3%', right: '4%', bottom: '10%', top: '15%', containLabel: true },
+    xAxis: { type: 'category', data: ['<15天', '15-30天', '30-45天', '45-60天', '>60天'] },
+    yAxis: { type: 'value', name: 'SKU数' },
+    series: [{
+      type: 'bar',
+      data: [
+        { value: 25, itemStyle: { color: '#11998e' } },
+        { value: 40, itemStyle: { color: '#38ef7d' } },
+        { value: 20, itemStyle: { color: '#fee140' } },
+        { value: 10, itemStyle: { color: '#f5576c' } },
+        { value: 5, itemStyle: { color: '#fa709a' } }
+      ],
+      barWidth: '50%'
+    }]
+  });
+};
+
+const initTurnoverGaugeChart = () => {
+  if (!turnoverGaugeRef.value) return;
+  turnoverGaugeChart = echarts.init(turnoverGaugeRef.value);
+  
+  turnoverGaugeChart.setOption({
+    title: { text: '综合周转指数', left: 'center', textStyle: { fontSize: 14, fontWeight: 500 } },
+    series: [{
+      type: 'gauge',
+      center: ['50%', '60%'],
+      startAngle: 200,
+      endAngle: -20,
+      min: 0,
+      max: 100,
+      splitNumber: 10,
+      itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: '#11998e' }, { offset: 0.5, color: '#fee140' }, { offset: 1, color: '#f5576c' }]) },
+      progress: { show: true, width: 20 },
+      pointer: { show: false },
+      axisLine: { lineStyle: { width: 20 } },
+      axisTick: { show: false },
+      splitLine: { show: false },
+      axisLabel: { show: false },
+      anchor: { show: false },
+      title: { show: false },
+      detail: { valueAnimation: true, fontSize: 28, offsetCenter: [0, '10%'], formatter: '{value}分', color: '#333' },
+      data: [{ value: 72 }]
+    }, {
+      type: 'pie',
+      radius: ['75%', '85%'],
+      center: ['50%', '60%'],
+      startAngle: 200,
+      endAngle: -20,
+      itemStyle: { color: '#f5f5f5' },
+      label: { show: false },
+      data: [{ value: 100 }]
+    }]
+  });
+};
+
+const initAllCharts = () => {
+  initTurnoverTrendChart();
+  initWarehouseComparisonChart();
+  initCategoryPieChart();
+  initStockDistributionChart();
+  initTurnoverGaugeChart();
+};
+
+const handleResize = () => {
+  turnoverTrendChart?.resize();
+  warehouseComparisonChart?.resize();
+  categoryPieChart?.resize();
+  stockDistributionChart?.resize();
+  turnoverGaugeChart?.resize();
+};
+
 const turnoverClass = (days: number) => {
   if (days > 60) return 'text-danger';
   if (days > 30) return 'text-warning';
@@ -156,18 +446,67 @@ const statusTag = (status: string) => {
   return map[status] || '';
 };
 
-const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
-const resetFilters = () => { filters.value = { sku: '', warehouse: '', dimension: '' }; };
+const avgAgeTag = (age: number) => {
+  if (age > 60) return 'danger';
+  if (age > 30) return 'warning';
+  return 'info';
+};
+
+const stockProgressStatus = (days: number) => {
+  if (days > 60) return 'exception';
+  if (days > 30) return 'warning';
+  return 'success';
+};
+
+const suggestionType = (suggestion: string) => {
+  const map: Record<string, string> = { '正常': 'info', '加仓': 'success', '促销': 'warning', '清仓': 'danger' };
+  return map[suggestion] || '';
+};
+
+const tableRowClass = ({ row }: { row: TurnoverItem }) => {
+  if (row.status === '严重滞销') return 'danger-row';
+  if (row.status === '滞销') return 'warning-row';
+  return '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; initAllCharts(); }, 300); };
+const resetFilters = () => { filters.value = { sku: '', warehouse: '', dimension: '', status: '' }; };
 const setAlert = () => { ElMessage.info('预警设置功能开发中'); };
 const doExport = () => { ElMessage.info('导出开始'); };
 const openDetail = (row: TurnoverItem) => { ElMessage.info(`查看 ${row.sku} 详情`); };
 const createOrder = (row: TurnoverItem) => { ElMessage.success(`已为 ${row.sku} 生成补货建议`); };
+const handleLiquidation = (row: TurnoverItem) => { ElMessage.warning(`已提交 ${row.sku} 清仓申请`); };
+const generateReplenishment = () => { ElMessage.success('已生成补货单,共 3 个SKU待处理'); };
+
+watch(timeRange, () => { initTurnoverTrendChart(); });
 
-onMounted(loadData);
+onMounted(() => {
+  loadData();
+  window.addEventListener('resize', handleResize);
+});
 </script>
 
 <style scoped>
 .filter-form :deep(.el-form-item) { margin-bottom: 0; }
-.text-danger { color: var(--cb-danger); font-weight: 600; }
-.text-warning { color: var(--cb-accent); font-weight: 600; }
+.section-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid #f0f0f0; }
+.section-header h3 { margin: 0; font-size: 15px; font-weight: 600; color: #333; }
+.chart-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; padding: 16px; }
+.chart-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; padding: 16px; }
+.chart-container { background: #fafafa; border-radius: 8px; padding: 8px; }
+.text-danger { color: #f5576c; font-weight: 600; }
+.text-warning { color: #fee140; font-weight: 600; }
+.stat-card { display: flex; align-items: center; gap: 12px; }
+.stat-card__icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
+.stat-card__icon svg { width: 24px; height: 24px; }
+.stat-card__content { flex: 1; min-width: 0; }
+.stat-card__label { font-size: 12px; color: #666; margin-bottom: 4px; }
+.stat-card__value { font-size: 24px; font-weight: 700; line-height: 1.2; }
+.stat-card__trend { font-size: 12px; margin-top: 4px; display: flex; align-items: center; gap: 2px; }
+.stat-card__trend.up { color: #11998e; }
+.stat-card__trend.down { color: #f5576c; }
+.stat-card__trend.neutral { color: #999; }
+.stat-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
+
+:deep(.danger-row) { background-color: #fff5f5 !important; }
+:deep(.warning-row) { background-color: #fffbf0 !important; }
 </style>

+ 23 - 1
src/views/supplier/PurchaseOrderView.vue

@@ -383,10 +383,32 @@ const handleArrivalSubmit = async () => {
   await arrivalFormRef.value.validate();
   submitting.value = true;
   try {
+    const po = items.value.find(p => p.id === arrivalForm.poId);
     await api.updatePurchaseOrder(arrivalForm.poId, {
       status: 'partial_arrival'
     } as Partial<PurchaseOrderItem>);
-    ElMessage.success('到货确认成功,库存已更新');
+    
+    const invRes = await api.getInventory();
+    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, {
+        available: newAvailable,
+        inTransit: newInTransit
+      });
+      await api.createInventoryLog({
+        source: '采购到货入库',
+        relatedOrder: po?.poNo || arrivalForm.poId,
+        operator: '采购',
+        quantity: arrivalForm.qty,
+        afterQty: newAvailable,
+        time: new Date().toISOString()
+      });
+      ElMessage.success(`到货确认成功,${arrivalForm.sku} 库存 +${arrivalForm.qty}`);
+    } else {
+      ElMessage.success('到货确认成功(未找到对应库存记录)');
+    }
     arrivalDialogVisible.value = false;
     loadData();
   } finally {