فهرست منبع

feat: 实现P1优先级页面(退款管理、供应商结算、促销活动、来料质检、采购需求、库存周转、消息中心、消息模板、审批流程、退件管理)

docker 2 ماه پیش
والد
کامیت
98048924f9

+ 203 - 0
src/views/finance/RefundView.vue

@@ -0,0 +1,203 @@
+<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.refundNo" placeholder="退款单号" clearable style="width:170px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="原订单号">
+          <el-input v-model="filters.orderNo" placeholder="原订单号" clearable style="width:170px" />
+        </el-form-item>
+        <el-form-item label="渠道">
+          <el-select v-model="filters.channel" placeholder="全部渠道" clearable style="width:130px">
+            <el-option label="Shopify" value="Shopify" />
+            <el-option label="TikTok Shop" value="TikTok Shop" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="退款状态">
+          <el-select v-model="filters.status" 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-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="openBatchRefund">批量退款</el-button>
+          <el-button @click="showReasonStats = !showReasonStats">{{ showReasonStats ? '隐藏' : '显示' }}原因统计</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section v-if="showReasonStats" class="glass-card section-card" style="padding:16px">
+      <h4 style="margin:0 0 12px">退款原因分析</h4>
+      <div class="stat-grid" style="grid-template-columns:repeat(4, 1fr)">
+        <article class="stat-card">
+          <div class="stat-card__label">商品破损</div>
+          <div class="stat-card__value" style="font-size:24px">23%</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">与描述不符</div>
+          <div class="stat-card__value" style="font-size:24px">31%</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">错发/漏发</div>
+          <div class="stat-card__value" style="font-size:24px">18%</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">其他原因</div>
+          <div class="stat-card__value" style="font-size:24px">28%</div>
+        </article>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading" @selection-change="onSelection">
+        <el-table-column type="selection" width="45" />
+        <el-table-column prop="refundNo" label="退款单号" width="170" />
+        <el-table-column prop="orderNo" label="原订单号" width="170">
+          <template #default="{ row }">
+            <el-button link type="primary">{{ row.orderNo }}</el-button>
+          </template>
+        </el-table-column>
+        <el-table-column prop="channel" label="渠道" width="110" />
+        <el-table-column prop="amount" label="退款金额" width="110">
+          <template #default="{ row }">
+            <span style="font-weight:600">{{ row.currency }} {{ row.amount }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="refundMethod" label="退款方式" width="100" />
+        <el-table-column prop="reason" label="退款原因" min-width="120" show-overflow-tooltip />
+        <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 prop="applyTime" label="申请时间" width="160" />
+        <el-table-column prop="refundTime" label="退款时间" width="160" />
+        <el-table-column label="操作" width="140" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="retryRefund(row)" v-if="row.status === '退款失败'">重试</el-button>
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="detailVisible" title="退款详情" width="560px">
+      <el-descriptions :column="1" border v-if="detailItem">
+        <el-descriptions-item label="退款单号">{{ detailItem.refundNo }}</el-descriptions-item>
+        <el-descriptions-item label="原订单号">{{ detailItem.orderNo }}</el-descriptions-item>
+        <el-descriptions-item label="渠道">{{ detailItem.channel }}</el-descriptions-item>
+        <el-descriptions-item label="退款金额">{{ detailItem.currency }} {{ detailItem.amount }}</el-descriptions-item>
+        <el-descriptions-item label="退款方式">{{ detailItem.refundMethod }}</el-descriptions-item>
+        <el-descriptions-item label="退款原因">{{ detailItem.reason }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="statusTag(detailItem.status)">{{ detailItem.status }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="申请时间">{{ detailItem.applyTime }}</el-descriptions-item>
+        <el-descriptions-item label="退款时间">{{ detailItem.refundTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="渠道退款单号">{{ detailItem.channelRefundNo || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="备注">{{ detailItem.remark || '-' }}</el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <el-button @click="detailVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface RefundItem {
+  refundNo: string;
+  orderNo: string;
+  channel: string;
+  currency: string;
+  amount: string;
+  refundMethod: string;
+  reason: string;
+  status: string;
+  applyTime: string;
+  refundTime: string;
+  channelRefundNo: string;
+  remark: string;
+}
+
+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: '' }
+]);
+
+const loading = ref(false);
+const selected = ref<RefundItem[]>([]);
+const detailVisible = ref(false);
+const detailItem = ref<RefundItem | null>(null);
+const showReasonStats = ref(false);
+
+const filters = ref({ refundNo: '', orderNo: '', channel: '', status: '' });
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.refundNo && !item.refundNo.includes(filters.value.refundNo)) return false;
+    if (filters.value.orderNo && !item.orderNo.includes(filters.value.orderNo)) return false;
+    if (filters.value.channel && item.channel !== filters.value.channel) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { '待退款': 'warning', '退款中': 'primary', '已退款': 'success', '退款失败': 'danger' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+
+const resetFilters = () => { filters.value = { refundNo: '', orderNo: '', channel: '', status: '' }; };
+
+const onSelection = (rows: RefundItem[]) => { selected.value = rows; };
+
+const openDetail = (row: RefundItem) => { detailItem.value = row; detailVisible.value = true; };
+
+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 = '退款中';
+  ElMessage.success('退款已重新发起');
+};
+
+const openBatchRefund = async () => {
+  if (!selected.value.length) { ElMessage.warning('请先选择要退款的记录'); return; }
+  await ElMessageBox.confirm(`确认对选中的 ${selected.value.length} 条记录发起退款?`, '批量退款');
+  for (const item of selected.value) {
+    const idx = items.value.findIndex(i => i.refundNo === item.refundNo);
+    if (idx !== -1) items.value[idx].status = '退款中';
+  }
+  ElMessage.success('批量退款已发起');
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 261 - 0
src/views/finance/SupplierSettlementView.vue

@@ -0,0 +1,261 @@
+<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.settlementNo" placeholder="结算单号" clearable style="width:170px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="供应商">
+          <el-select v-model="filters.supplier" placeholder="全部供应商" clearable style="width:160px">
+            <el-option v-for="s in suppliers" :key="s" :label="s" :value="s" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-select v-model="filters.status" 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-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 @click="doExport">导出</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="settlementNo" label="结算单号" width="170" />
+        <el-table-column prop="supplier" label="供应商" min-width="160" />
+        <el-table-column prop="period" label="结算周期" width="180" />
+        <el-table-column prop="poCount" label="采购单数" width="90" align="center" />
+        <el-table-column prop="totalAmount" label="应付金额" width="120">
+          <template #default="{ row }">
+            <span style="font-weight:600">{{ row.currency }} {{ row.totalAmount }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="paidAmount" label="已付金额" width="120">
+          <template #default="{ row }">
+            {{ row.currency }} {{ row.paidAmount }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="90">
+          <template #default="{ row }">
+            <el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="dueDate" label="到期日" width="110" />
+        <el-table-column prop="createdAt" label="创建时间" width="160" />
+        <el-table-column label="操作" width="200" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="primary" @click="openPayment(row)" v-if="row.status === '待付款'">付款</el-button>
+            <el-button link type="primary" @click="closeSettlement(row)" v-if="row.status === '待确认'">确认</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </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__label">本月应付</div>
+          <div class="stat-card__value" style="font-size:24px;color:var(--cb-primary)">$128,500</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">本月已付</div>
+          <div class="stat-card__value" style="font-size:24px">$86,200</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">待付款</div>
+          <div class="stat-card__value" style="font-size:24px;color:var(--cb-accent)">$42,300</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">逾期未付</div>
+          <div class="stat-card__value" style="font-size:24px;color:var(--cb-danger)">$8,500</div>
+        </article>
+      </div>
+    </section>
+
+    <el-dialog v-model="detailVisible" title="结算单详情" width="700px" destroy-on-close>
+      <el-descriptions :column="2" border v-if="detailItem">
+        <el-descriptions-item label="结算单号">{{ detailItem.settlementNo }}</el-descriptions-item>
+        <el-descriptions-item label="供应商">{{ detailItem.supplier }}</el-descriptions-item>
+        <el-descriptions-item label="结算周期" :span="2">{{ detailItem.period }}</el-descriptions-item>
+        <el-descriptions-item label="应付金额">{{ detailItem.currency }} {{ detailItem.totalAmount }}</el-descriptions-item>
+        <el-descriptions-item label="已付金额">{{ detailItem.currency }} {{ detailItem.paidAmount }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="statusTag(detailItem.status)">{{ detailItem.status }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="到期日">{{ detailItem.dueDate }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间" :span="2">{{ detailItem.createdAt }}</el-descriptions-item>
+      </el-descriptions>
+      <el-table :data="poList" stripe size="small" style="margin-top:16px">
+        <el-table-column prop="poNo" label="采购单号" width="150" />
+        <el-table-column prop="poAmount" label="采购金额" width="100" />
+        <el-table-column prop="status" label="状态" width="80" />
+      </el-table>
+    </el-dialog>
+
+    <el-dialog v-model="paymentVisible" title="付款确认" width="500px" destroy-on-close>
+      <el-form :model="paymentForm" label-width="100px">
+        <el-form-item label="结算单号">
+          <el-input :value="detailItem?.settlementNo" disabled />
+        </el-form-item>
+        <el-form-item label="付款金额">
+          <el-input :value="`${detailItem?.currency} ${detailItem?.totalAmount}`" disabled />
+        </el-form-item>
+        <el-form-item label="付款方式" required>
+          <el-select v-model="paymentForm.method" placeholder="选择付款方式" style="width:100%">
+            <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-upload action="#" :auto-upload="false">
+            <el-button size="small">上传凭证</el-button>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="paymentForm.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="paymentVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmPayment">确认付款</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="createVisible" title="新建结算单" width="560px" destroy-on-close>
+      <el-form :model="createForm" label-width="100px">
+        <el-form-item label="供应商" required>
+          <el-select v-model="createForm.supplier" placeholder="选择供应商" style="width:100%">
+            <el-option v-for="s in suppliers" :key="s" :label="s" :value="s" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="结算周期" required>
+          <el-date-picker v-model="createForm.period" type="daterange" range-separator="至" start-placeholder="开始" end-placeholder="结束" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="关联采购单">
+          <el-select v-model="createForm.poNos" multiple placeholder="选择采购单" style="width:100%">
+            <el-option label="PO-20260415-001 - $12,500" value="PO-20260415-001" />
+            <el-option label="PO-20260410-002 - $8,300" value="PO-20260410-002" />
+            <el-option label="PO-20260405-003 - $15,200" value="PO-20260405-003" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="到期日">
+          <el-date-picker v-model="createForm.dueDate" type="date" placeholder="选择到期日" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="createForm.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="createVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmCreate">确认创建</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface SettlementItem {
+  settlementNo: string;
+  supplier: string;
+  period: string;
+  poCount: number;
+  currency: string;
+  totalAmount: string;
+  paidAmount: string;
+  status: string;
+  dueDate: string;
+  createdAt: string;
+}
+
+const items = ref<SettlementItem[]>([
+  { settlementNo: 'ST-202604-001', supplier: '深圳鼎力供应商', period: '2026-04-01 至 2026-04-15', poCount: 3, currency: 'USD', totalAmount: '25,800', paidAmount: '25,800', status: '已付款', dueDate: '2026-04-30', createdAt: '2026-04-16 10:00:00' },
+  { settlementNo: 'ST-202604-002', supplier: '义乌恒达皮具', period: '2026-04-01 至 2026-04-15', poCount: 2, currency: 'USD', totalAmount: '18,500', paidAmount: '10,000', status: '待付款', dueDate: '2026-04-28', createdAt: '2026-04-16 14:30:00' },
+  { settlementNo: 'ST-202604-003', supplier: '深圳鼎力供应商', period: '2026-04-16 至 2026-04-20', poCount: 2, currency: 'USD', totalAmount: '15,200', paidAmount: '0', status: '待确认', dueDate: '2026-05-05', createdAt: '2026-04-20 09:00:00' },
+  { settlementNo: 'ST-202603-005', supplier: '美国西部仓', period: '2026-03-01 至 2026-03-31', poCount: 4, currency: 'USD', totalAmount: '32,000', paidAmount: '32,000', status: '已付款', dueDate: '2026-04-15', createdAt: '2026-04-01 11:00:00' }
+]);
+
+const poList = ref([{ poNo: 'PO-20260415-001', poAmount: '12,500', status: '已完结' }, { poNo: 'PO-20260410-002', poAmount: '8,300', status: '已完结' }]);
+
+const suppliers = ['深圳鼎力供应商', '义乌恒达皮具', '美国西部仓', '云途物流'];
+const loading = ref(false);
+const detailVisible = ref(false);
+const paymentVisible = ref(false);
+const createVisible = ref(false);
+const detailItem = ref<SettlementItem | null>(null);
+
+const filters = ref({ settlementNo: '', supplier: '', status: '' });
+
+const paymentForm = reactive({ method: '', remark: '' });
+const createForm = reactive({ supplier: '', period: '', poNos: [] as string[], dueDate: '', remark: '' });
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.settlementNo && !item.settlementNo.includes(filters.value.settlementNo)) return false;
+    if (filters.value.supplier && item.supplier !== filters.value.supplier) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { '待确认': 'warning', '待付款': 'primary', '已付款': 'success', '已关闭': 'info' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { settlementNo: '', supplier: '', status: '' }; };
+
+const openDetail = (row: SettlementItem) => { detailItem.value = row; detailVisible.value = true; };
+const openPayment = (row: SettlementItem) => { detailItem.value = row; Object.assign(paymentForm, { method: '', remark: '' }); paymentVisible.value = true; };
+const closeSettlement = async (row: SettlementItem) => { await ElMessageBox.confirm('确认此结算单?', '确认'); const idx = items.value.findIndex(i => i.settlementNo === row.settlementNo); if (idx !== -1) items.value[idx].status = '待付款'; ElMessage.success('已确认'); };
+
+const confirmPayment = async () => {
+  if (!paymentForm.method) { ElMessage.warning('请选择付款方式'); return; }
+  const idx = items.value.findIndex(i => i.settlementNo === detailItem.value!.settlementNo);
+  if (idx !== -1) items.value[idx].status = '已付款';
+  paymentVisible.value = false;
+  ElMessage.success('付款已确认');
+};
+
+const openCreate = () => { Object.assign(createForm, { supplier: '', period: '', poNos: [], dueDate: '', remark: '' }); createVisible.value = true; };
+
+const confirmCreate = async () => {
+  if (!createForm.supplier || !createForm.period) { ElMessage.warning('请填写必填项'); return; }
+  items.value.unshift({ settlementNo: `ST-${new Date().toISOString().slice(0, 7).replace(/-/g, '')}-${String(items.value.length + 1).padStart(3, '0')}`, supplier: createForm.supplier, period: '自定义周期', poCount: createForm.poNos.length, currency: 'USD', totalAmount: '0', paidAmount: '0', status: '待确认', dueDate: '', createdAt: new Date().toLocaleString() });
+  createVisible.value = false;
+  ElMessage.success('结算单已创建');
+};
+
+const doExport = () => { ElMessage.info('导出开始'); };
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 222 - 0
src/views/marketing/PromotionView.vue

@@ -0,0 +1,222 @@
+<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.type" 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 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>
+        </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="openDialog()">新建活动</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="name" label="活动名称" min-width="180" />
+        <el-table-column prop="type" label="活动类型" width="90">
+          <template #default="{ row }">
+            <el-tag size="small">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="channels" label="适用渠道" min-width="140">
+          <template #default="{ row }">
+            <el-tag v-for="c in row.channels" :key="c" size="small" style="margin-right:4px">{{ c }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="timeRange" label="活动时间" min-width="200" />
+        <el-table-column prop="discount" label="优惠内容" width="120">
+          <template #default="{ row }">
+            <span style="color:var(--cb-primary);font-weight:600">{{ row.discount }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="90">
+          <template #default="{ row }">
+            <el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="orderCount" label="参与订单" width="100" align="center" />
+        <el-table-column prop="salesAmount" label="销售额提升" width="110" align="center" />
+        <el-table-column label="操作" width="160" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDialog(row)">编辑</el-button>
+            <el-button link :type="row.status === '进行中' ? 'danger' : 'primary'" @click="toggleStatus(row)">{{ row.status === '进行中' ? '终止' : '启用' }}</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="dialogVisible" :title="editingItem ? '编辑活动' : '新建活动'" width="600px" destroy-on-close>
+      <el-form :model="formData" label-width="100px">
+        <el-form-item label="活动名称" required>
+          <el-input v-model="formData.name" placeholder="请输入活动名称" />
+        </el-form-item>
+        <el-form-item label="活动类型" required>
+          <el-select v-model="formData.type" placeholder="选择类型" style="width:100%">
+            <el-option label="折扣" value="折扣" />
+            <el-option label="满减" value="满减" />
+            <el-option label="买赠" value="买赠" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="适用渠道" required>
+          <el-checkbox-group v-model="formData.channels">
+            <el-checkbox label="Shopify" />
+            <el-checkbox label="TikTok Shop" />
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="优惠规则" required>
+          <el-input v-model="formData.discount" placeholder="如:满100减10 / 8折 / 买二赠一" />
+        </el-form-item>
+        <el-form-item label="开始时间" required>
+          <el-date-picker v-model="formData.startTime" type="datetime" placeholder="选择开始时间" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="结束时间" required>
+          <el-date-picker v-model="formData.endTime" type="datetime" placeholder="选择结束时间" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="适用商品">
+          <el-select v-model="formData.scopeType" placeholder="选择适用范围" style="width:100%">
+            <el-option label="全部商品" value="all" />
+            <el-option label="指定类目" value="category" />
+            <el-option label="指定商品" value="product" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="活动描述">
+          <el-input v-model="formData.description" type="textarea" :rows="3" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="savePromotion">保存</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface PromotionItem {
+  id: string;
+  name: string;
+  type: string;
+  channels: string[];
+  timeRange: string;
+  discount: string;
+  status: string;
+  orderCount: number;
+  salesAmount: string;
+  startTime: string;
+  endTime: string;
+  scopeType: string;
+  description: string;
+}
+
+const items = ref<PromotionItem[]>([
+  { id: 'P001', name: '夏季清仓大促', type: '折扣', channels: ['Shopify', 'TikTok Shop'], timeRange: '2026-04-01 至 2026-04-30', discount: '全场8折', status: '进行中', orderCount: 1256, salesAmount: '+35%', startTime: '2026-04-01 00:00', endTime: '2026-04-30 23:59', scopeType: 'all', description: '夏季清仓,全场8折优惠' },
+  { id: 'P002', name: '母亲节满减活动', type: '满减', channels: ['Shopify'], timeRange: '2026-05-01 至 2026-05-10', discount: '满200减30', status: '未开始', orderCount: 0, salesAmount: '-', startTime: '2026-05-01 00:00', endTime: '2026-05-10 23:59', scopeType: 'category', description: '母亲节特别活动' },
+  { id: 'P003', name: '新品首发买赠', type: '买赠', channels: ['TikTok Shop'], timeRange: '2026-04-15 至 2026-04-25', discount: '买二赠一', status: '已结束', orderCount: 456, salesAmount: '+18%', startTime: '2026-04-15 00:00', endTime: '2026-04-25 23:59', scopeType: 'product', description: '新品首发,购买任意2件赠1件' }
+]);
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const editingItem = ref<PromotionItem | null>(null);
+
+const filters = ref({ name: '', type: '', status: '' });
+
+const formData = reactive({
+  name: '',
+  type: '',
+  channels: [] as string[],
+  discount: '',
+  startTime: '',
+  endTime: '',
+  scopeType: 'all',
+  description: ''
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.name && !item.name.includes(filters.value.name)) return false;
+    if (filters.value.type && item.type !== filters.value.type) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { '未开始': 'info', '进行中': 'success', '已结束': '' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { name: '', type: '', status: '' }; };
+
+const openDialog = (item?: PromotionItem) => {
+  editingItem.value = item || null;
+  if (item) {
+    Object.assign(formData, { name: item.name, type: item.type, channels: [...item.channels], discount: item.discount, startTime: item.startTime, endTime: item.endTime, scopeType: item.scopeType, description: item.description });
+  } else {
+    Object.assign(formData, { name: '', type: '', channels: [], discount: '', startTime: '', endTime: '', scopeType: 'all', description: '' });
+  }
+  dialogVisible.value = true;
+};
+
+const savePromotion = () => {
+  if (!formData.name || !formData.type || !formData.channels.length || !formData.discount) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  if (editingItem.value) {
+    const idx = items.value.findIndex(i => i.id === editingItem.value!.id);
+    if (idx !== -1) items.value[idx] = { ...items.value[idx], ...formData };
+    ElMessage.success('活动已更新');
+  } else {
+    items.value.push({ id: `P${String(items.value.length + 1).padStart(3, '0')}`, ...formData, timeRange: `${formData.startTime} 至 ${formData.endTime}`, status: '未开始', orderCount: 0, salesAmount: '-' });
+    ElMessage.success('活动已创建');
+  }
+  dialogVisible.value = false;
+};
+
+const toggleStatus = async (row: PromotionItem) => {
+  if (row.status === '进行中') {
+    await ElMessageBox.confirm('确认终止此活动?', '终止确认');
+    const idx = items.value.findIndex(i => i.id === row.id);
+    if (idx !== -1) items.value[idx].status = '已结束';
+    ElMessage.success('活动已终止');
+  }
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 231 - 0
src/views/procurement/IQCView.vue

@@ -0,0 +1,231 @@
+<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.iqcNo" placeholder="质检单号" clearable style="width:170px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="供应商">
+          <el-select v-model="filters.supplier" placeholder="全部供应商" clearable style="width:160px">
+            <el-option v-for="s in suppliers" :key="s" :label="s" :value="s" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="质检结果">
+          <el-select v-model="filters.result" placeholder="全部" clearable style="width:120px">
+            <el-option label="合格" value="pass" />
+            <el-option label="不合格" value="fail" />
+            <el-option label="待检" value="pending" />
+          </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>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="iqcNo" label="质检单号" width="170" />
+        <el-table-column prop="arrivalNo" label="到货单号" width="160" />
+        <el-table-column prop="supplier" label="供应商" min-width="160" />
+        <el-table-column prop="sku" label="SKU" width="150" />
+        <el-table-column prop="arrivalQty" label="到货数量" width="90" align="center" />
+        <el-table-column prop="sampleQty" label="抽检数量" width="90" align="center" />
+        <el-table-column prop="qualifiedQty" label="合格数" width="80" align="center">
+          <template #default="{ row }">
+            <span style="color:var(--cb-success)">{{ row.qualifiedQty }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="unqualifiedQty" label="不合格数" width="90" align="center">
+          <template #default="{ row }">
+            <span v-if="row.unqualifiedQty > 0" style="color:var(--cb-danger)">{{ row.unqualifiedQty }}</span>
+            <span v-else>0</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="result" label="结果" width="80">
+          <template #default="{ row }">
+            <el-tag :type="resultTag(row.result)" size="small">{{ resultLabel(row.result) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="inspector" label="质检员" width="100" />
+        <el-table-column prop="inspectTime" label="质检时间" width="160" />
+        <el-table-column label="操作" width="160" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="primary" v-if="row.result === 'pending'" @click="openInspect(row)">执行质检</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="inspectDialogVisible" title="执行质检" width="600px" destroy-on-close>
+      <el-form :model="inspectForm" label-width="100px">
+        <el-descriptions :column="2" border style="margin-bottom:16px">
+          <el-descriptions-item label="质检单号">{{ currentItem?.iqcNo }}</el-descriptions-item>
+          <el-descriptions-item label="到货数量">{{ currentItem?.arrivalQty }}</el-descriptions-item>
+          <el-descriptions-item label="SKU">{{ currentItem?.sku }}</el-descriptions-item>
+          <el-descriptions-item label="抽检数量">{{ currentItem?.sampleQty }}</el-descriptions-item>
+        </el-descriptions>
+        <el-form-item label="合格数量" required>
+          <el-input-number v-model="inspectForm.qualifiedQty" :min="0" :max="currentItem?.sampleQty || 0" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="不合格数量">
+          <el-input-number v-model="inspectForm.unqualifiedQty" :min="0" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="不合格原因">
+          <el-select v-model="inspectForm.failReason" multiple placeholder="选择不合格原因" 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="inspectForm.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="inspectDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmInspect">确认提交</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="detailDialogVisible" title="质检详情" width="560px">
+      <el-descriptions :column="1" border v-if="detailItem">
+        <el-descriptions-item label="质检单号">{{ detailItem.iqcNo }}</el-descriptions-item>
+        <el-descriptions-item label="到货单号">{{ detailItem.arrivalNo }}</el-descriptions-item>
+        <el-descriptions-item label="供应商">{{ detailItem.supplier }}</el-descriptions-item>
+        <el-descriptions-item label="SKU">{{ detailItem.sku }}</el-descriptions-item>
+        <el-descriptions-item label="到货数量">{{ detailItem.arrivalQty }}</el-descriptions-item>
+        <el-descriptions-item label="抽检数量">{{ detailItem.sampleQty }}</el-descriptions-item>
+        <el-descriptions-item label="合格数量">{{ detailItem.qualifiedQty }}</el-descriptions-item>
+        <el-descriptions-item label="不合格数量">{{ detailItem.unqualifiedQty }}</el-descriptions-item>
+        <el-descriptions-item label="质检结果">
+          <el-tag :type="resultTag(detailItem.result)">{{ resultLabel(detailItem.result) }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="质检员">{{ detailItem.inspector }}</el-descriptions-item>
+        <el-descriptions-item label="质检时间">{{ detailItem.inspectTime }}</el-descriptions-item>
+        <el-descriptions-item label="处理方式">{{ detailItem.handleMethod || '-' }}</el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <el-button @click="detailDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage } from 'element-plus';
+
+interface IQCItem {
+  iqcNo: string;
+  arrivalNo: string;
+  supplier: string;
+  sku: string;
+  arrivalQty: number;
+  sampleQty: number;
+  qualifiedQty: number;
+  unqualifiedQty: number;
+  result: string;
+  inspector: string;
+  inspectTime: string;
+  failReason?: string;
+  handleMethod?: string;
+}
+
+const items = ref<IQCItem[]>([
+  { iqcNo: 'IQC-20260420-001', arrivalNo: 'AR-20260420-001', supplier: '深圳鼎力供应商', sku: 'SKU-LUGG-20-BLK', arrivalQty: 500, sampleQty: 20, qualifiedQty: 19, unqualifiedQty: 1, result: 'pass', inspector: '质检员A', inspectTime: '2026-04-20 11:30:00', failReason: '外观瑕疵', handleMethod: '让步接收' },
+  { iqcNo: 'IQC-20260419-002', arrivalNo: 'AR-20260419-001', supplier: '义乌恒达皮具', sku: 'SKU-BAG-ML-BRW', arrivalQty: 200, sampleQty: 15, qualifiedQty: 14, unqualifiedQty: 1, result: 'pass', inspector: '质检员A', inspectTime: '2026-04-19 16:45:00' },
+  { iqcNo: 'IQC-20260418-003', arrivalNo: 'AR-20260418-001', supplier: '深圳鼎力供应商', sku: 'SKU-SPRT-YGA-BLU', arrivalQty: 1000, sampleQty: 30, qualifiedQty: 0, unqualifiedQty: 0, result: 'pending', inspector: '-', inspectTime: '-' },
+  { iqcNo: 'IQC-20260417-004', arrivalNo: 'AR-20260417-001', supplier: '美国西部仓', sku: 'SKU-LUGG-28-NVY', arrivalQty: 100, sampleQty: 10, qualifiedQty: 8, unqualifiedQty: 2, result: 'fail', inspector: '质检员B', inspectTime: '2026-04-17 14:20:00', failReason: '拉链损坏', handleMethod: '退货' }
+]);
+
+const suppliers = ['深圳鼎力供应商', '义乌恒达皮具', '美国西部仓'];
+const loading = ref(false);
+const inspectDialogVisible = ref(false);
+const detailDialogVisible = ref(false);
+const currentItem = ref<IQCItem | null>(null);
+const detailItem = ref<IQCItem | null>(null);
+
+const filters = ref({ iqcNo: '', supplier: '', result: '' });
+
+const inspectForm = reactive({
+  qualifiedQty: 0,
+  unqualifiedQty: 0,
+  failReason: [] as string[],
+  remark: ''
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.iqcNo && !item.iqcNo.includes(filters.value.iqcNo)) return false;
+    if (filters.value.supplier && item.supplier !== filters.value.supplier) return false;
+    if (filters.value.result && item.result !== filters.value.result) return false;
+    return true;
+  });
+});
+
+const resultTag = (result: string) => {
+  const map: Record<string, string> = { 'pass': 'success', 'fail': 'danger', 'pending': 'warning' };
+  return map[result] || '';
+};
+
+const resultLabel = (result: string) => {
+  const map: Record<string, string> = { 'pass': '合格', 'fail': '不合格', 'pending': '待检' };
+  return map[result] || result;
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { iqcNo: '', supplier: '', result: '' }; };
+
+const openCreate = () => { ElMessage.info('新建质检单功能开发中'); };
+
+const openInspect = (row: IQCItem) => {
+  currentItem.value = row;
+  Object.assign(inspectForm, { qualifiedQty: row.sampleQty, unqualifiedQty: 0, failReason: [], remark: '' });
+  inspectDialogVisible.value = true;
+};
+
+const confirmInspect = () => {
+  const passRate = inspectForm.qualifiedQty / currentItem.value!.sampleQty;
+  const result = passRate >= 0.95 ? 'pass' : 'fail';
+  const idx = items.value.findIndex(i => i.iqcNo === currentItem.value!.iqcNo);
+  if (idx !== -1) {
+    items.value[idx] = {
+      ...items.value[idx],
+      qualifiedQty: inspectForm.qualifiedQty,
+      unqualifiedQty: inspectForm.unqualifiedQty,
+      result,
+      inspector: '当前用户',
+      inspectTime: new Date().toLocaleString(),
+      failReason: inspectForm.failReason.join(','),
+      handleMethod: result === 'pass' ? '入库' : '退货/降价接收'
+    };
+  }
+  inspectDialogVisible.value = false;
+  ElMessage.success('质检已完成');
+};
+
+const openDetail = (row: IQCItem) => { detailItem.value = row; detailDialogVisible.value = true; };
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 234 - 0
src/views/procurement/PurchaseRequestView.vue

@@ -0,0 +1,234 @@
+<template>
+  <div class="app-page">
+    <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-form-item>
+        <el-form-item label="供应商">
+          <el-select v-model="filters.supplier" placeholder="全部供应商" clearable style="width:160px">
+            <el-option v-for="s in suppliers" :key="s" :label="s" :value="s" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-select v-model="filters.status" placeholder="全部" clearable style="width:120px">
+            <el-option label="待审批" value="pending" />
+            <el-option label="已通过" value="approved" />
+            <el-option label="已驳回" value="rejected" />
+            <el-option label="已采购" value="purchased" />
+          </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>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="requestNo" label="需求单号" width="170" />
+        <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="qty" label="需求数量" width="90" align="center" />
+        <el-table-column prop="expectedDate" label="期望交期" width="110" />
+        <el-table-column prop="priority" label="紧急程度" width="90">
+          <template #default="{ row }">
+            <el-tag :type="priorityTag(row.priority)" size="small">{{ row.priority }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="90">
+          <template #default="{ row }">
+            <el-tag :type="statusTag(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="creator" label="申请人" width="100" />
+        <el-table-column prop="createTime" label="申请时间" width="160" />
+        <el-table-column label="操作" width="160" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="primary" v-if="row.status === 'pending'" @click="approveRequest(row)">审批</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="createDialogVisible" title="新建采购需求" width="560px" destroy-on-close>
+      <el-form :model="createForm" label-width="100px">
+        <el-form-item label="SKU" required>
+          <el-select v-model="createForm.sku" placeholder="选择SKU" style="width:100%" filterable>
+            <el-option v-for="s in skuOptions" :key="s.sku" :label="`${s.sku} - ${s.title}`" :value="s.sku" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="需求数量" required>
+          <el-input-number v-model="createForm.qty" :min="1" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="期望交期" required>
+          <el-date-picker v-model="createForm.expectedDate" type="date" placeholder="选择日期" style="width:100%" />
+        </el-form-item>
+        <el-form-item label="紧急程度" required>
+          <el-select v-model="createForm.priority" placeholder="选择紧急程度" style="width:100%">
+            <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="需求原因" required>
+          <el-input v-model="createForm.reason" type="textarea" :rows="3" placeholder="请填写需求原因" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="createDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmCreate">提交申请</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="detailDialogVisible" title="需求详情" width="500px" destroy-on-close>
+      <el-descriptions :column="1" border v-if="detailItem">
+        <el-descriptions-item label="需求单号">{{ detailItem.requestNo }}</el-descriptions-item>
+        <el-descriptions-item label="SKU">{{ detailItem.sku }}</el-descriptions-item>
+        <el-descriptions-item label="商品名称">{{ detailItem.productTitle }}</el-descriptions-item>
+        <el-descriptions-item label="需求数量">{{ detailItem.qty }}</el-descriptions-item>
+        <el-descriptions-item label="期望交期">{{ detailItem.expectedDate }}</el-descriptions-item>
+        <el-descriptions-item label="紧急程度">
+          <el-tag :type="priorityTag(detailItem.priority)">{{ detailItem.priority }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="statusTag(detailItem.status)">{{ statusLabel(detailItem.status) }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="申请人">{{ detailItem.creator }}</el-descriptions-item>
+        <el-descriptions-item label="申请时间">{{ detailItem.createTime }}</el-descriptions-item>
+        <el-descriptions-item label="需求原因">{{ detailItem.reason }}</el-descriptions-item>
+        <el-descriptions-item v-if="detailItem.approveRemark" label="审批备注">{{ detailItem.approveRemark }}</el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <el-button @click="detailDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface PurchaseRequestItem {
+  requestNo: string;
+  sku: string;
+  productTitle: string;
+  qty: number;
+  expectedDate: string;
+  priority: string;
+  status: string;
+  creator: string;
+  createTime: string;
+  reason: string;
+  approveRemark?: string;
+}
+
+const items = ref<PurchaseRequestItem[]>([
+  { requestNo: 'PR-20260420-001', sku: 'SKU-LUGG-20-BLK', productTitle: 'TravelFlex Carry-On / Black', qty: 100, expectedDate: '2026-04-30', priority: '紧急', status: 'pending', creator: '张明', createTime: '2026-04-20 10:30:00', reason: '库存不足,即将断货' },
+  { requestNo: 'PR-20260419-002', sku: 'SKU-SPRT-YGA-BLU', productTitle: 'Yoga Mat Pro / Blue', qty: 200, expectedDate: '2026-05-05', priority: '高', status: 'approved', creator: '王磊', createTime: '2026-04-19 14:20:00', reason: '促销活动备货' },
+  { requestNo: 'PR-20260419-003', sku: 'SKU-BAG-BPK-OLV', productTitle: 'Urban Backpack / Olive', qty: 50, expectedDate: '2026-05-10', priority: '中', status: 'rejected', creator: '李华', createTime: '2026-04-19 09:15:00', reason: '正常补货', approveRemark: '库存充足,暂缓采购' },
+  { requestNo: 'PR-20260418-004', sku: 'SKU-TOWEL-SET-MIX', productTitle: 'AeroDry Towel Set', qty: 150, expectedDate: '2026-04-28', priority: '高', status: 'purchased', creator: '张明', createTime: '2026-04-18 11:00:00', reason: '海外仓补货' }
+]);
+
+const suppliers = ['深圳鼎力供应商', '义乌恒达皮具', '美国西部仓'];
+const skuOptions = [
+  { sku: 'SKU-LUGG-20-BLK', title: 'TravelFlex Carry-On / Black' },
+  { sku: 'SKU-BAG-ML-BRW', title: 'Classic Leather Tote / Brown' },
+  { sku: 'SKU-SPRT-YGA-BLU', title: 'Yoga Mat Pro / Blue' }
+];
+
+const loading = ref(false);
+const createDialogVisible = ref(false);
+const detailDialogVisible = ref(false);
+const detailItem = ref<PurchaseRequestItem | null>(null);
+
+const filters = ref({ sku: '', supplier: '', status: '' });
+
+const createForm = reactive({
+  sku: '',
+  qty: 1,
+  expectedDate: '',
+  priority: '',
+  reason: ''
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.sku && !item.sku.includes(filters.value.sku)) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const priorityTag = (priority: string) => {
+  const map: Record<string, string> = { '紧急': 'danger', '高': 'warning', '中': '', '低': 'info' };
+  return map[priority] || '';
+};
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { 'pending': 'warning', 'approved': 'success', 'rejected': 'danger', 'purchased': 'info' };
+  return map[status] || '';
+};
+
+const statusLabel = (status: string) => {
+  const map: Record<string, string> = { 'pending': '待审批', 'approved': '已通过', 'rejected': '已驳回', 'purchased': '已采购' };
+  return map[status] || status;
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { sku: '', supplier: '', status: '' }; };
+
+const openCreate = () => { Object.assign(createForm, { sku: '', qty: 1, expectedDate: '', priority: '', reason: '' }); createDialogVisible.value = true; };
+
+const confirmCreate = async () => {
+  if (!createForm.sku || !createForm.qty || !createForm.expectedDate || !createForm.priority || !createForm.reason) {
+    ElMessage.warning('请填写完整信息');
+    return;
+  }
+  items.value.unshift({
+    requestNo: `PR-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}-${String(items.value.length + 1).padStart(3, '0')}`,
+    sku: createForm.sku,
+    productTitle: skuOptions.find(s => s.sku === createForm.sku)?.title || '',
+    qty: createForm.qty,
+    expectedDate: createForm.expectedDate,
+    priority: createForm.priority,
+    status: 'pending',
+    creator: '当前用户',
+    createTime: new Date().toLocaleString(),
+    reason: createForm.reason
+  });
+  createDialogVisible.value = false;
+  ElMessage.success('需求已提交');
+};
+
+const openDetail = (row: PurchaseRequestItem) => { detailItem.value = row; detailDialogVisible.value = true; };
+
+const approveRequest = async (row: PurchaseRequestItem) => {
+  await ElMessageBox.confirm('确认通过此采购需求?', '审批确认');
+  const idx = items.value.findIndex(i => i.requestNo === row.requestNo);
+  if (idx !== -1) items.value[idx].status = 'approved';
+  ElMessage.success('已通过审批');
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 173 - 0
src/views/report/InventoryTurnoverView.vue

@@ -0,0 +1,173 @@
+<template>
+  <div class="app-page">
+    <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-form-item>
+        <el-form-item label="仓库">
+          <el-select v-model="filters.warehouse" placeholder="全部仓库" clearable style="width:140px">
+            <el-option v-for="w in warehouses" :key="w" :label="w" :value="w" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="分析维度">
+          <el-select v-model="filters.dimension" placeholder="全部" clearable style="width:130px">
+            <el-option label="按SKU" value="sku" />
+            <el-option label="按类目" value="category" />
+            <el-option label="按仓库" value="warehouse" />
+          </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="setAlert">设置预警</el-button>
+          <el-button @click="doExport">导出报表</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <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>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">动销率</div>
+          <div class="stat-card__value" style="font-size:24px">76%</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>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">库存覆盖天数</div>
+          <div class="stat-card__value" style="font-size:24px">42天</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">平均库龄</div>
+          <div class="stat-card__value" style="font-size:24px">35天</div>
+        </article>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <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="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">
+          <template #default="{ row }">
+            <span :class="turnoverClass(row.turnoverDays)">{{ row.turnoverDays }}天</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="stockDays" label="库存覆盖天数" width="110" align="center">
+          <template #default="{ row }">
+            <span :class="stockDaysClass(row.stockDays)">{{ row.stockDays }}天</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="avgAge" label="平均库龄" width="90" align="center">
+          <template #default="{ row }">
+            {{ row.avgAge }}天
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="90">
+          <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">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="primary" @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 { ElMessage } from 'element-plus';
+
+interface TurnoverItem {
+  sku: string;
+  productTitle: string;
+  warehouse: string;
+  available: number;
+  sales30d: number;
+  turnoverDays: number;
+  stockDays: number;
+  avgAge: number;
+  status: 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: '严重滞销' }
+]);
+
+const loading = ref(false);
+const warehouses = ['深圳南山仓', '义乌商贸仓', '洛杉矶海外仓'];
+
+const filters = ref({ sku: '', warehouse: '', dimension: '' });
+
+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.warehouse && item.warehouse !== filters.value.warehouse) return false;
+    return true;
+  });
+});
+
+const turnoverClass = (days: number) => {
+  if (days > 60) return 'text-danger';
+  if (days > 30) return 'text-warning';
+  return '';
+};
+
+const stockDaysClass = (days: number) => {
+  if (days < 10) return 'text-danger';
+  if (days < 20) return 'text-warning';
+  return '';
+};
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { '正常': 'success', '滞销': 'warning', '严重滞销': 'danger' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { sku: '', warehouse: '', dimension: '' }; };
+const setAlert = () => { ElMessage.info('预警设置功能开发中'); };
+const doExport = () => { ElMessage.info('导出开始'); };
+const openDetail = (row: TurnoverItem) => { ElMessage.info(`查看 ${row.sku} 详情`); };
+const createOrder = (row: TurnoverItem) => { ElMessage.success(`已为 ${row.sku} 生成补货建议`); };
+
+onMounted(loadData);
+</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; }
+</style>

+ 213 - 0
src/views/system/ApprovalFlowView.vue

@@ -0,0 +1,213 @@
+<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.type" placeholder="全部" clearable style="width:140px">
+            <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.status" placeholder="全部" clearable style="width:120px">
+            <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>
+
+    <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="openDialog()">新建流程</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="name" label="流程名称" min-width="180" />
+        <el-table-column prop="type" label="流程类型" width="120">
+          <template #default="{ row }">
+            <el-tag size="small">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="nodeCount" label="审批节点" width="90" align="center" />
+        <el-table-column prop="nodes" label="审批流程" min-width="250">
+          <template #default="{ row }">
+            <el-tag v-for="(node, idx) in row.nodeList" :key="idx" :type="idx === 0 ? 'primary' : 'info'" size="small" style="margin-right:4px">
+              {{ node }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="80">
+          <template #default="{ row }">
+            <el-tag :type="row.status === '启用' ? 'success' : 'danger'" size="small">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="updatedAt" label="更新时间" width="160" />
+        <el-table-column label="操作" width="180" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDialog(row)">编辑</el-button>
+            <el-button link :type="row.status === '启用' ? 'danger' : 'primary'" @click="toggleStatus(row)">{{ row.status === '启用' ? '停用' : '启用' }}</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="dialogVisible" :title="editingItem ? '编辑审批流程' : '新建审批流程'" width="600px" destroy-on-close>
+      <el-form :model="formData" label-width="90px">
+        <el-form-item label="流程名称" required>
+          <el-input v-model="formData.name" placeholder="请输入流程名称" />
+        </el-form-item>
+        <el-form-item label="流程类型" required>
+          <el-select v-model="formData.type" placeholder="选择流程类型" style="width:100%">
+            <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="审批节点" required>
+          <div v-for="(node, idx) in formData.nodes" :key="idx" style="display:flex;gap:8px;margin-bottom:8px">
+            <el-input v-model="formData.nodes[idx]" placeholder="节点名称" style="flex:1" />
+            <el-select v-model="formData.nodeTypes[idx]" placeholder="审批方式" style="width:120px">
+              <el-option label="单人审批" value="单人" />
+              <el-option label="会签" value="会签" />
+              <el-option label="或签" value="或签" />
+            </el-select>
+            <el-button type="danger" @click="removeNode(idx)" :disabled="formData.nodes.length <= 1">删除</el-button>
+          </div>
+          <el-button type="primary" plain size="small" @click="addNode">+ 添加节点</el-button>
+        </el-form-item>
+        <el-form-item label="适用组织">
+          <el-select v-model="formData.orgScope" multiple placeholder="选择适用组织" style="width:100%">
+            <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="formData.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveFlow">保存</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface ApprovalFlow {
+  id: string;
+  name: string;
+  type: string;
+  nodeCount: number;
+  nodeList: string[];
+  nodes: { name: string; type: string }[];
+  status: string;
+  updatedAt: string;
+  orgScope: string[];
+  remark?: string;
+}
+
+const items = ref<ApprovalFlow[]>([
+  { id: 'AF001', name: '采购单审批流程', type: '采购审批', nodeCount: 3, nodeList: ['采购主管'], status: '启用', nodes: [{ name: '采购主管', type: '单人' }, { name: '财务总监', type: '单人' }, { name: '总经理', type: '单人' }], updatedAt: '2026-04-15 10:30:00', orgScope: ['采购部', '财务部'] },
+  { id: 'AF002', name: '小额费用报销', type: '费用报销', nodeCount: 2, nodeList: ['部门主管'], status: '启用', nodes: [{ name: '部门主管', type: '单人' }, { name: '财务', type: '会签' }], updatedAt: '2026-04-10 14:20:00', orgScope: ['全公司'] },
+  { id: 'AF003', name: '合同审批流程', type: '合同审批', nodeCount: 4, nodeList: ['法务'], status: '启用', nodes: [{ name: '法务', type: '单人' }, { name: '财务总监', type: '单人' }, { name: '运营总监', type: '单人' }, { name: '总经理', type: '或签' }], updatedAt: '2026-04-08 09:00:00', orgScope: ['运营部', '财务部'] },
+  { id: 'AF004', name: '请假申请流程', type: '请假申请', nodeCount: 2, nodeList: ['部门主管'], status: '停用', nodes: [{ name: '部门主管', type: '单人' }, { name: 'HR', type: '单人' }], updatedAt: '2026-03-28 16:45:00', orgScope: ['全公司'] }
+]);
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const editingItem = ref<ApprovalFlow | null>(null);
+
+const filters = ref({ name: '', type: '', status: '' });
+
+const formData = reactive({
+  name: '',
+  type: '',
+  nodes: ['部门主管'] as string[],
+  nodeTypes: ['单人'] as string[],
+  orgScope: [] as string[],
+  remark: ''
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.name && !item.name.includes(filters.value.name)) return false;
+    if (filters.value.type && item.type !== filters.value.type) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { name: '', type: '', status: '' }; };
+
+const addNode = () => { formData.nodes.push(''); formData.nodeTypes.push('单人'); };
+const removeNode = (idx: number) => { formData.nodes.splice(idx, 1); formData.nodeTypes.splice(idx, 1); };
+
+const openDialog = (item?: ApprovalFlow) => {
+  editingItem.value = item || null;
+  if (item) {
+    Object.assign(formData, { name: item.name, type: item.type, nodes: [...item.nodeList], nodeTypes: item.nodes.map(n => n.type), orgScope: [...item.orgScope], remark: item.remark || '' });
+  } else {
+    Object.assign(formData, { name: '', type: '', nodes: [''], nodeTypes: ['单人'], orgScope: [], remark: '' });
+  }
+  dialogVisible.value = true;
+};
+
+const saveFlow = () => {
+  if (!formData.name || !formData.type || !formData.nodes.length) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  const nodeList = formData.nodes.filter(Boolean);
+  if (editingItem.value) {
+    const idx = items.value.findIndex(i => i.id === editingItem.value!.id);
+    if (idx !== -1) {
+      items.value[idx] = { ...items.value[idx], name: formData.name, type: formData.type, nodeCount: nodeList.length, nodeList, nodes: formData.nodes.map((n, i) => ({ name: n, type: formData.nodeTypes[i] })), orgScope: formData.orgScope, updatedAt: new Date().toLocaleString() };
+    }
+    ElMessage.success('流程已更新');
+  } else {
+    items.value.push({ id: `AF${String(items.value.length + 1).padStart(3, '0')}`, name: formData.name, type: formData.type, nodeCount: nodeList.length, nodeList, nodes: formData.nodes.map((n, i) => ({ name: n, type: formData.nodeTypes[i] })), status: '启用', updatedAt: new Date().toLocaleString(), orgScope: formData.orgScope });
+    ElMessage.success('流程已创建');
+  }
+  dialogVisible.value = false;
+};
+
+const toggleStatus = async (row: ApprovalFlow) => {
+  const action = row.status === '启用' ? '停用' : '启用';
+  await ElMessageBox.confirm(`确认${action}「${row.name}」?`);
+  const idx = items.value.findIndex(i => i.id === row.id);
+  if (idx !== -1) items.value[idx].status = row.status === '启用' ? '停用' : '启用';
+  ElMessage.success(`流程已${action}`);
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 206 - 0
src/views/system/MessageTemplateView.vue

@@ -0,0 +1,206 @@
+<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.type" 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 label="状态">
+          <el-select v-model="filters.status" placeholder="全部" clearable style="width:120px">
+            <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>
+
+    <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="openDialog()">新建模板</el-button>
+        </div>
+        <el-button @click="loadData">刷新</el-button>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="name" label="模板名称" min-width="160" />
+        <el-table-column prop="type" label="类型" width="80">
+          <template #default="{ row }">
+            <el-tag size="small">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="channel" label="渠道" width="120" />
+        <el-table-column prop="subject" label="主题" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="variables" label="变量" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="status" label="状态" width="80">
+          <template #default="{ row }">
+            <el-tag :type="row.status === '启用' ? 'success' : 'danger'" size="small">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="updatedAt" label="更新时间" width="160" />
+        <el-table-column label="操作" width="180" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDialog(row)">编辑</el-button>
+            <el-button link :type="row.status === '启用' ? 'danger' : 'primary'" @click="toggleStatus(row)">{{ row.status === '启用' ? '停用' : '启用' }}</el-button>
+            <el-button link type="primary" @click="doCopy(row)">复制</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="dialogVisible" :title="editingItem ? '编辑模板' : '新建模板'" width="600px" destroy-on-close>
+      <el-form :model="formData" label-width="90px">
+        <el-form-item label="模板名称" required>
+          <el-input v-model="formData.name" placeholder="请输入模板名称" />
+        </el-form-item>
+        <el-form-item label="类型" required>
+          <el-select v-model="formData.type" placeholder="选择类型" style="width:100%">
+            <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="formData.channel" placeholder="选择渠道" style="width:100%">
+            <el-option label="通用" value="通用" />
+            <el-option label="Shopify" value="Shopify" />
+            <el-option label="TikTok Shop" value="TikTok Shop" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="模板主题" v-if="formData.type !== '短信'">
+          <el-input v-model="formData.subject" placeholder="邮件主题/站内信标题" />
+        </el-form-item>
+        <el-form-item label="模板内容" required>
+          <el-input v-model="formData.content" type="textarea" :rows="6" placeholder="模板内容,支持变量占位符:{{orderNo}}、{{buyerName}}等" />
+        </el-form-item>
+        <el-form-item label="可用变量">
+          <div style="color:var(--cb-text-soft);font-size:13px">
+            {{orderNo}} 订单号 | {{buyerName}} 买家姓名 | {{orderAmount}} 订单金额 | {{trackingNo}} 运单号 | {{sku}} SKU | {{productName}} 商品名称
+          </div>
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="formData.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveTemplate">保存</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface MessageTemplate {
+  id: string;
+  name: string;
+  type: string;
+  channel: string;
+  subject: string;
+  content: string;
+  variables: string;
+  status: string;
+  updatedAt: string;
+  remark?: string;
+}
+
+const items = ref<MessageTemplate[]>([
+  { id: 'MT001', name: '订单发货通知', type: '邮件', channel: '通用', subject: '您的订单已发货 - {{orderNo}}', content: '亲爱的 {{buyerName}},\n\n您的订单 {{orderNo}} 已于今日发货,预计3-5个工作日送达。\n\n物流信息:{{trackingNo}}\n\n感谢您的购买!', variables: '{{orderNo}}, {{buyerName}}, {{trackingNo}}', status: '启用', updatedAt: '2026-04-15 10:30:00' },
+  { id: 'MT002', name: '发货提醒短信', type: '短信', channel: '通用', subject: '', content: '【CrossBorder】亲爱的{{buyerName}},您的订单{{orderNo}}已发货,运单号{{trackingNo}},请注意查收。如有疑问请联系客服。', variables: '{{orderNo}}, {{buyerName}}, {{trackingNo}}', status: '启用', updatedAt: '2026-04-10 14:20:00' },
+  { id: 'MT003', name: '退款确认通知', type: '邮件', channel: '通用', subject: '退款已完成 - {{orderNo}}', content: '亲爱的 {{buyerName}},\n\n您的退款申请已处理完成。\n\n退款单号:{{refundNo}}\n退款金额:{{refundAmount}}\n\n预计1-7个工作日到账,感谢您的理解。', variables: '{{orderNo}}, {{buyerName}}, {{refundNo}}, {{refundAmount}}', status: '启用', updatedAt: '2026-04-08 09:00:00' },
+  { id: 'MT004', name: '缺货提醒', type: '站内信', channel: 'Shopify', subject: '库存不足提醒', content: '您好,SKU {{sku}} ({{productName}}) 当前库存不足,请及时补货。', variables: '{{sku}}, {{productName}}', status: '停用', updatedAt: '2026-03-28 16:45:00' }
+]);
+
+const loading = ref(false);
+const dialogVisible = ref(false);
+const editingItem = ref<MessageTemplate | null>(null);
+
+const filters = ref({ name: '', type: '', status: '' });
+
+const formData = reactive({
+  name: '',
+  type: '',
+  channel: '通用',
+  subject: '',
+  content: '',
+  remark: ''
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.name && !item.name.includes(filters.value.name)) return false;
+    if (filters.value.type && item.type !== filters.value.type) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    return true;
+  });
+});
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+const resetFilters = () => { filters.value = { name: '', type: '', status: '' }; };
+
+const openDialog = (item?: MessageTemplate) => {
+  editingItem.value = item || null;
+  if (item) {
+    Object.assign(formData, { name: item.name, type: item.type, channel: item.channel, subject: item.subject, content: item.content, remark: item.remark || '' });
+  } else {
+    Object.assign(formData, { name: '', type: '', channel: '通用', subject: '', content: '', remark: '' });
+  }
+  dialogVisible.value = true;
+};
+
+const saveTemplate = () => {
+  if (!formData.name || !formData.type || !formData.content) {
+    ElMessage.warning('请填写必填项');
+    return;
+  }
+  if (editingItem.value) {
+    const idx = items.value.findIndex(i => i.id === editingItem.value!.id);
+    if (idx !== -1) items.value[idx] = { ...items.value[idx], ...formData, updatedAt: new Date().toLocaleString() };
+    ElMessage.success('模板已更新');
+  } else {
+    items.value.push({ id: `MT${String(items.value.length + 1).padStart(3, '0')}`, ...formData, variables: '{{orderNo}}, {{buyerName}}', status: '启用', updatedAt: new Date().toLocaleString() });
+    ElMessage.success('模板已创建');
+  }
+  dialogVisible.value = false;
+};
+
+const toggleStatus = async (row: MessageTemplate) => {
+  const action = row.status === '启用' ? '停用' : '启用';
+  await ElMessageBox.confirm(`确认${action}「${row.name}」?`);
+  const idx = items.value.findIndex(i => i.id === row.id);
+  if (idx !== -1) items.value[idx].status = row.status === '启用' ? '停用' : '启用';
+  ElMessage.success(`模板已${action}`);
+};
+
+const doCopy = (row: MessageTemplate) => {
+  ElMessageBox.confirm(`确认复制「${row.name}」模板?`).then(() => {
+    items.value.push({ ...row, id: `MT${String(items.value.length + 1).padStart(3, '0')}`, name: `${row.name} (副本)`, status: '停用', updatedAt: new Date().toLocaleString() });
+    ElMessage.success('模板已复制');
+  }).catch(() => {});
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 189 - 0
src/views/system/NotificationView.vue

@@ -0,0 +1,189 @@
+<template>
+  <div class="app-page">
+    <section class="glass-card section-card">
+      <div class="table-toolbar">
+        <div class="chip-list">
+          <el-button type="primary" @click="openSend">发送通知</el-button>
+        </div>
+        <el-form :model="filters" inline>
+          <el-form-item label="类型">
+            <el-select v-model="filters.type" placeholder="全部" clearable style="width:120px">
+              <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.readStatus" placeholder="全部" clearable style="width:120px">
+              <el-option label="未读" value="unread" />
+              <el-option label="已读" value="read" />
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column type="selection" width="45" />
+        <el-table-column prop="type" label="类型" width="100">
+          <template #default="{ row }">
+            <el-tag :type="typeTag(row.type)" size="small">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="title" label="标题" min-width="250" />
+        <el-table-column prop="content" label="内容" min-width="300" show-overflow-tooltip />
+        <el-table-column prop="sender" label="发送人" width="100" />
+        <el-table-column prop="time" label="时间" width="160" />
+        <el-table-column prop="readStatus" label="状态" width="80">
+          <template #default="{ row }">
+            <span v-if="row.readStatus === '未读'" style="color:var(--cb-primary);font-weight:600">未读</span>
+            <span v-else style="color:var(--cb-text-soft)">已读</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="120" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">查看</el-button>
+            <el-button link type="danger" @click="deleteNotice(row)">删除</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="sendDialogVisible" title="发送通知" width="500px" destroy-on-close>
+      <el-form :model="sendForm" label-width="80px">
+        <el-form-item label="接收人" required>
+          <el-select v-model="sendForm.receiverType" placeholder="选择接收范围" style="width:100%">
+            <el-option label="全部用户" value="all" />
+            <el-option label="指定角色" value="role" />
+            <el-option label="指定用户" value="user" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="消息类型" required>
+          <el-select v-model="sendForm.type" placeholder="选择类型" style="width:100%">
+            <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="标题" required>
+          <el-input v-model="sendForm.title" placeholder="通知标题" />
+        </el-form-item>
+        <el-form-item label="内容" required>
+          <el-input v-model="sendForm.content" type="textarea" :rows="4" placeholder="通知内容" />
+        </el-form-item>
+        <el-form-item label="重要">
+          <el-switch v-model="sendForm.important" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="sendDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmSend">发送</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="detailDialogVisible" title="通知详情" width="500px">
+      <el-descriptions :column="1" border v-if="detailItem">
+        <el-descriptions-item label="类型">
+          <el-tag :type="typeTag(detailItem.type)" size="small">{{ detailItem.type }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="标题">{{ detailItem.title }}</el-descriptions-item>
+        <el-descriptions-item label="内容">{{ detailItem.content }}</el-descriptions-item>
+        <el-descriptions-item label="发送人">{{ detailItem.sender }}</el-descriptions-item>
+        <el-descriptions-item label="时间">{{ detailItem.time }}</el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <el-button @click="detailDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface NoticeItem {
+  id: string;
+  type: string;
+  title: string;
+  content: string;
+  sender: string;
+  time: string;
+  readStatus: string;
+}
+
+const items = ref<NoticeItem[]>([
+  { id: 'N001', type: '系统通知', title: '系统维护公告', content: '平台将于2026-04-25 02:00-04:00进行系统维护,届时部分功能将暂停使用。', sender: '系统管理员', time: '2026-04-20 10:00:00', readStatus: '未读' },
+  { id: 'N002', type: '业务通知', title: '新订单提醒', content: '您有5笔新订单待处理,请及时登录系统查看。', sender: 'OMS系统', time: '2026-04-20 09:30:00', readStatus: '未读' },
+  { id: 'N003', type: '待办提醒', title: '采购单待审批', content: '您有待审批的采购单 PO-20260420-003,请及时处理。', sender: '采购系统', time: '2026-04-20 09:00:00', readStatus: '已读' },
+  { id: 'N004', type: '公告', title: '五一劳动节放假安排', content: '五一假期期间(5月1日-5月5日),仓库暂停发货,4月30日中午12点后的订单将于5月6日起陆续发货。', sender: '运营部', time: '2026-04-19 15:00:00', readStatus: '已读' },
+  { id: 'N005', type: '业务通知', title: '库存预警', content: 'SKU-LUGG-20-BLK 当前库存15件,低于安全库存50件,请及时补货。', sender: '库存系统', time: '2026-04-19 14:30:00', readStatus: '已读' }
+]);
+
+const loading = ref(false);
+const sendDialogVisible = ref(false);
+const detailDialogVisible = ref(false);
+const detailItem = ref<NoticeItem | null>(null);
+
+const filters = ref({ type: '', readStatus: '' });
+
+const sendForm = reactive({
+  receiverType: 'all',
+  type: '',
+  title: '',
+  content: '',
+  important: false
+});
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.type && item.type !== filters.value.type) return false;
+    if (filters.value.readStatus && item.readStatus !== (filters.value.readStatus === 'read' ? '已读' : '未读')) return false;
+    return true;
+  });
+});
+
+const typeTag = (type: string) => {
+  const map: Record<string, string> = { '系统通知': 'info', '业务通知': 'success', '待办提醒': 'warning', '公告': '' };
+  return map[type] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+
+const openSend = () => { Object.assign(sendForm, { receiverType: 'all', type: '', title: '', content: '', important: false }); sendDialogVisible.value = true; };
+
+const confirmSend = () => {
+  if (!sendForm.type || !sendForm.title || !sendForm.content) { ElMessage.warning('请填写必填项'); return; }
+  items.value.unshift({ id: `N${String(items.value.length + 1).padStart(3, '0')}`, type: sendForm.type, title: sendForm.title, content: sendForm.content, sender: '当前用户', time: new Date().toLocaleString(), readStatus: '未读' });
+  sendDialogVisible.value = false;
+  ElMessage.success('通知已发送');
+};
+
+const openDetail = (row: NoticeItem) => {
+  detailItem.value = row;
+  if (row.readStatus === '未读') {
+    const idx = items.value.findIndex(i => i.id === row.id);
+    if (idx !== -1) items.value[idx].readStatus = '已读';
+  }
+  detailDialogVisible.value = true;
+};
+
+const deleteNotice = async (row: NoticeItem) => {
+  await ElMessageBox.confirm('确认删除此通知?', '删除确认');
+  const idx = items.value.findIndex(i => i.id === row.id);
+  if (idx !== -1) items.value.splice(idx, 1);
+  ElMessage.success('通知已删除');
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>

+ 247 - 0
src/views/warehouse/ReturnPackageView.vue

@@ -0,0 +1,247 @@
+<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.returnNo" placeholder="退件单号" clearable style="width:170px" @keyup.enter="loadData" />
+        </el-form-item>
+        <el-form-item label="原运单号">
+          <el-input v-model="filters.trackingNo" placeholder="原运单号" clearable style="width:160px" />
+        </el-form-item>
+        <el-form-item label="退件状态">
+          <el-select v-model="filters.status" 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 label="仓库">
+          <el-select v-model="filters.warehouse" placeholder="全部仓库" clearable style="width:140px">
+            <el-option v-for="w in warehouses" :key="w" :label="w" :value="w" />
+          </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="openClaim">退件认领</el-button>
+          <el-button @click="loadData">刷新</el-button>
+        </div>
+        <el-button @click="showStats = !showStats">{{ showStats ? '隐藏' : '显示' }}统计</el-button>
+      </div>
+    </section>
+
+    <section v-if="showStats" 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__label">本月退件</div>
+          <div class="stat-card__value" style="font-size:24px">156</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">本月退件率</div>
+          <div class="stat-card__value" style="font-size:24px">2.3%</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">待处理</div>
+          <div class="stat-card__value" style="font-size:24px;color:var(--cb-accent)">23</div>
+        </article>
+        <article class="stat-card">
+          <div class="stat-card__label">可售库存</div>
+          <div class="stat-card__value" style="font-size:24px;color:var(--cb-primary)">89</div>
+        </article>
+      </div>
+    </section>
+
+    <section class="glass-card section-card">
+      <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
+        <el-table-column prop="returnNo" label="退件单号" width="170" />
+        <el-table-column prop="originalTrackingNo" label="原运单号" width="160" />
+        <el-table-column prop="channel" label="渠道" width="100" />
+        <el-table-column prop="returnReason" label="退件原因" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="warehouse" label="仓库" width="120" />
+        <el-table-column prop="status" label="状态" width="90">
+          <template #default="{ row }">
+            <el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="returnQty" label="件数" width="70" align="center" />
+        <el-table-column prop="receiveTime" label="收货时间" width="160" />
+        <el-table-column prop="productStatus" label="商品状态" width="100">
+          <template #default="{ row }">
+            <el-tag :type="productStatusTag(row.productStatus)" size="small">{{ row.productStatus }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="handler" label="处理人" width="100" />
+        <el-table-column label="操作" width="160" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" @click="openDetail(row)">详情</el-button>
+            <el-button link type="primary" @click="openProcess(row)" v-if="row.status !== '已完成'">处理</el-button>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <el-empty description="暂无数据" />
+        </template>
+      </el-table>
+    </section>
+
+    <el-dialog v-model="claimDialogVisible" title="退件认领" width="500px" destroy-on-close>
+      <el-form :model="claimForm" label-width="100px">
+        <el-form-item label="扫描运单号" required>
+          <el-input v-model="claimForm.trackingNo" placeholder="扫描或输入运单号" />
+        </el-form-item>
+        <el-form-item label="退件原因" required>
+          <el-select v-model="claimForm.returnReason" placeholder="选择退件原因" 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="仓库" required>
+          <el-select v-model="claimForm.warehouse" placeholder="选择仓库" style="width:100%">
+            <el-option v-for="w in warehouses" :key="w" :label="w" :value="w" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input v-model="claimForm.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="claimDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmClaim">确认认领</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="processDialogVisible" title="退件处理" width="560px" destroy-on-close>
+      <el-form :model="processForm" label-width="100px">
+        <el-descriptions :column="2" border style="margin-bottom:16px" v-if="currentItem">
+          <el-descriptions-item label="退件单号">{{ currentItem.returnNo }}</el-descriptions-item>
+          <el-descriptions-item label="原运单号">{{ currentItem.originalTrackingNo }}</el-descriptions-item>
+          <el-descriptions-item label="退件原因">{{ currentItem.returnReason }}</el-descriptions-item>
+          <el-descriptions-item label="件数">{{ currentItem.returnQty }}</el-descriptions-item>
+        </el-descriptions>
+        <el-form-item label="商品状态" required>
+          <el-select v-model="processForm.productStatus" placeholder="选择商品状态" style="width:100%">
+            <el-option label="可售" value="可售" />
+            <el-option label="不可售" value="不可售" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="处理方式" required v-if="processForm.productStatus === '不可售'">
+          <el-select v-model="processForm.handleMethod" placeholder="选择处理方式" style="width:100%">
+            <el-option label="销毁" value="销毁" />
+            <el-option label="退还供应商" value="退还供应商" />
+            <el-option label="降价处理" value="降价处理" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="重新上架" v-if="processForm.productStatus === '可售'">
+          <el-switch v-model="processForm.relist" />
+        </el-form-item>
+        <el-form-item label="处理备注">
+          <el-input v-model="processForm.remark" type="textarea" :rows="2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="processDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmProcess">确认处理</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+
+interface ReturnPackageItem {
+  returnNo: string;
+  originalTrackingNo: string;
+  channel: string;
+  returnReason: string;
+  warehouse: string;
+  status: string;
+  returnQty: number;
+  receiveTime: string;
+  productStatus: string;
+  handler: string;
+}
+
+const items = ref<ReturnPackageItem[]>([
+  { returnNo: 'RT-20260420-001', originalTrackingNo: '1Z999AA10123456784', channel: 'Shopify', returnReason: '买家拒收', warehouse: '洛杉矶海外仓', status: '待认领', returnQty: 1, receiveTime: '-', productStatus: '-', handler: '-' },
+  { returnNo: 'RT-20260419-002', originalTrackingNo: '1Z999AA10123456785', channel: 'TikTok Shop', returnReason: '超时未取', warehouse: '洛杉矶海外仓', status: '处理中', returnQty: 2, receiveTime: '2026-04-19 14:30', productStatus: '可售', handler: 'Jack' },
+  { returnNo: 'RT-20260419-003', originalTrackingNo: 'SF1234567890', channel: 'Shopify', returnReason: '商品破损', warehouse: '深圳南山仓', status: '已完成', returnQty: 1, receiveTime: '2026-04-18 10:20', productStatus: '不可售', handler: '张明' },
+  { returnNo: 'RT-20260418-004', originalTrackingNo: 'DHL12345678', channel: 'Shopify', returnReason: '地址错误', warehouse: '伦敦海外仓', status: '已完成', returnQty: 3, receiveTime: '2026-04-17 16:00', productStatus: '可售', handler: 'Tom' },
+  { returnNo: 'RT-20260418-005', originalTrackingNo: '1Z999AA10123456786', channel: 'TikTok Shop', returnReason: '买家拒收', warehouse: '洛杉矶海外仓', status: '处理中', returnQty: 1, receiveTime: '2026-04-18 09:15', productStatus: '不可售', handler: 'Jack' }
+]);
+
+const loading = ref(false);
+const showStats = ref(true);
+const claimDialogVisible = ref(false);
+const processDialogVisible = ref(false);
+const currentItem = ref<ReturnPackageItem | null>(null);
+const warehouses = ['深圳南山仓', '义乌商贸仓', '洛杉矶海外仓', '伦敦海外仓'];
+
+const filters = ref({ returnNo: '', trackingNo: '', status: '', warehouse: '' });
+
+const claimForm = reactive({ trackingNo: '', returnReason: '', warehouse: '', remark: '' });
+
+const processForm = reactive({ productStatus: '', handleMethod: '', relist: true, remark: '' });
+
+const filteredItems = computed(() => {
+  return items.value.filter(item => {
+    if (filters.value.returnNo && !item.returnNo.includes(filters.value.returnNo)) return false;
+    if (filters.value.trackingNo && !item.originalTrackingNo.includes(filters.value.trackingNo)) return false;
+    if (filters.value.status && item.status !== filters.value.status) return false;
+    if (filters.value.warehouse && item.warehouse !== filters.value.warehouse) return false;
+    return true;
+  });
+});
+
+const statusTag = (status: string) => {
+  const map: Record<string, string> = { '待认领': 'warning', '处理中': 'primary', '已完成': 'success' };
+  return map[status] || '';
+};
+
+const productStatusTag = (status: string) => {
+  const map: Record<string, string> = { '可售': 'success', '不可售': 'danger' };
+  return map[status] || '';
+};
+
+const loadData = () => { loading.value = true; setTimeout(() => { loading.value = false; }, 300); };
+
+const resetFilters = () => { filters.value = { returnNo: '', trackingNo: '', status: '', warehouse: '' }; };
+
+const openClaim = () => { Object.assign(claimForm, { trackingNo: '', returnReason: '', warehouse: '', remark: '' }); claimDialogVisible.value = true; };
+
+const confirmClaim = async () => {
+  if (!claimForm.trackingNo || !claimForm.returnReason || !claimForm.warehouse) { ElMessage.warning('请填写完整信息'); return; }
+  items.value.unshift({ returnNo: `RT-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}-${String(items.value.length + 1).padStart(3, '0')}`, originalTrackingNo: claimForm.trackingNo, channel: 'Shopify', returnReason: claimForm.returnReason, warehouse: claimForm.warehouse, status: '待认领', returnQty: 1, receiveTime: '-', productStatus: '-', handler: '-' });
+  claimDialogVisible.value = false;
+  ElMessage.success('退件已认领');
+};
+
+const openDetail = (row: ReturnPackageItem) => { currentItem.value = row; };
+
+const openProcess = (row: ReturnPackageItem) => { currentItem.value = row; Object.assign(processForm, { productStatus: '', handleMethod: '', relist: true, remark: '' }); processDialogVisible.value = true; };
+
+const confirmProcess = async () => {
+  if (!processForm.productStatus) { ElMessage.warning('请选择商品状态'); return; }
+  if (processForm.productStatus === '不可售' && !processForm.handleMethod) { ElMessage.warning('请选择处理方式'); return; }
+  const idx = items.value.findIndex(i => i.returnNo === currentItem.value!.returnNo);
+  if (idx !== -1) items.value[idx] = { ...items.value[idx], status: '已完成', productStatus: processForm.productStatus, handler: '当前用户' };
+  processDialogVisible.value = false;
+  ElMessage.success('退件处理完成');
+};
+
+onMounted(loadData);
+</script>
+
+<style scoped>
+.filter-form :deep(.el-form-item) { margin-bottom: 0; }
+</style>