|
@@ -32,7 +32,7 @@
|
|
|
|
|
|
|
|
<!-- 售后单列表 -->
|
|
<!-- 售后单列表 -->
|
|
|
<section class="glass-card section-card">
|
|
<section class="glass-card section-card">
|
|
|
- <el-table :data="filteredItems" stripe style="width:100%">
|
|
|
|
|
|
|
+ <el-table :data="filteredItems" stripe style="width:100%" v-loading="loading">
|
|
|
<el-table-column prop="afterSaleNo" label="售后单号" width="180" />
|
|
<el-table-column prop="afterSaleNo" label="售后单号" width="180" />
|
|
|
<el-table-column prop="orderNo" label="订单号" width="190">
|
|
<el-table-column prop="orderNo" label="订单号" width="190">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
@@ -65,10 +65,19 @@
|
|
|
<el-button link type="danger" @click="openAudit(row, 'reject')">拒绝</el-button>
|
|
<el-button link type="danger" @click="openAudit(row, 'reject')">拒绝</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
<el-button link type="primary" v-if="row.auditStatus === '已通过' && row.type === '退款' && row.refundStatus === '未退款'" @click="doRefund(row)">发起退款</el-button>
|
|
<el-button link type="primary" v-if="row.auditStatus === '已通过' && row.type === '退款' && row.refundStatus === '未退款'" @click="doRefund(row)">发起退款</el-button>
|
|
|
|
|
+ <el-button link type="primary" v-if="row.auditStatus === '已通过' && row.type === '退货退款' && row.refundStatus === '未退款'" @click="openReturnTracking(row)">录入退货物流</el-button>
|
|
|
|
|
+ <el-button link type="primary" v-if="row.auditStatus === '已通过' && row.type === '退货退款' && row.refundStatus === '已退货待入库'" @click="confirmReceipt(row)">确认入库</el-button>
|
|
|
|
|
+ <el-button link type="primary" v-if="row.auditStatus === '已通过' && row.type === '换货' && row.refundStatus !== '已补发'" @click="openResend(row)">生成补发单</el-button>
|
|
|
<el-button link @click="openDetail(row)">详情</el-button>
|
|
<el-button link @click="openDetail(row)">详情</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
|
|
+ <template #empty>
|
|
|
|
|
+ <el-empty description="暂无数据" />
|
|
|
|
|
+ </template>
|
|
|
</el-table>
|
|
</el-table>
|
|
|
|
|
+ <div style="display:flex;justify-content:flex-end;margin-top:16px" v-if="total > 0">
|
|
|
|
|
+ <el-pagination v-model:current-page="page" v-model:page-size="pageSize" :total="total" :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" @size-change="loadData" @current-change="loadData" />
|
|
|
|
|
+ </div>
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|
|
|
<!-- 审核弹窗 -->
|
|
<!-- 审核弹窗 -->
|
|
@@ -90,18 +99,77 @@
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
+ <!-- 退货物流弹窗 -->
|
|
|
|
|
+ <el-dialog v-model="returnDialog" title="录入退货物流信息" width="500px">
|
|
|
|
|
+ <el-form label-position="top">
|
|
|
|
|
+ <el-form-item label="退货物流公司">
|
|
|
|
|
+ <el-select v-model="returnCarrier" style="width:100%">
|
|
|
|
|
+ <el-option label="DHL" value="DHL" />
|
|
|
|
|
+ <el-option label="FedEx" value="FedEx" />
|
|
|
|
|
+ <el-option label="顺丰" value="顺丰" />
|
|
|
|
|
+ <el-option label="其他" value="其他" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="退货物流单号" required>
|
|
|
|
|
+ <el-input v-model="returnTrackingNo" placeholder="请输入物流单号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="returnDialog = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="confirmReturnTracking">确认提交</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 生成补发单弹窗 -->
|
|
|
|
|
+ <el-dialog v-model="resendDialog" title="生成补发单" width="500px">
|
|
|
|
|
+ <el-form label-position="top">
|
|
|
|
|
+ <el-form-item label="补发仓库" required>
|
|
|
|
|
+ <el-select v-model="resendWarehouse" style="width:100%">
|
|
|
|
|
+ <el-option label="深圳仓" value="深圳仓" />
|
|
|
|
|
+ <el-option label="义乌仓" value="义乌仓" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="补发 SKU" required>
|
|
|
|
|
+ <el-select v-model="resendSku" style="width:100%">
|
|
|
|
|
+ <el-option label="SKU-LUGG-20-BLK - TravelFlex Carry-On Black" value="SKU-LUGG-20-BLK" />
|
|
|
|
|
+ <el-option label="SKU-BAG-08-GRY - Commuter Sling Bag Gray" value="SKU-BAG-08-GRY" />
|
|
|
|
|
+ <el-option label="SKU-TOWEL-SET-MIX - AeroDry Towel Set" value="SKU-TOWEL-SET-MIX" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="补发数量">
|
|
|
|
|
+ <el-input-number v-model="resendQty" :min="1" style="width:100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="备注">
|
|
|
|
|
+ <el-input v-model="resendRemark" type="textarea" :rows="2" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="resendDialog = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="confirmResend">确认生成补发单</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
<!-- 详情抽屉 -->
|
|
<!-- 详情抽屉 -->
|
|
|
<el-drawer v-model="detailDrawer" title="售后详情" size="480px">
|
|
<el-drawer v-model="detailDrawer" title="售后详情" size="480px">
|
|
|
<template v-if="detailItem">
|
|
<template v-if="detailItem">
|
|
|
<el-descriptions :column="1" border>
|
|
<el-descriptions :column="1" border>
|
|
|
<el-descriptions-item label="售后单号">{{ detailItem.afterSaleNo }}</el-descriptions-item>
|
|
<el-descriptions-item label="售后单号">{{ detailItem.afterSaleNo }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="关联订单">{{ detailItem.orderNo }}</el-descriptions-item>
|
|
|
|
|
|
|
+ <el-descriptions-item label="关联订单">
|
|
|
|
|
+ <el-button link type="primary" @click="$router.push(`/order/detail?id=${detailItem.orderNo}`)">{{ detailItem.orderNo }}</el-button>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
<el-descriptions-item label="买家">{{ detailItem.buyer }}</el-descriptions-item>
|
|
<el-descriptions-item label="买家">{{ detailItem.buyer }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="售后类型">{{ detailItem.type }}</el-descriptions-item>
|
|
<el-descriptions-item label="售后类型">{{ detailItem.type }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="申请金额">{{ detailItem.amount }}</el-descriptions-item>
|
|
<el-descriptions-item label="申请金额">{{ detailItem.amount }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="原因">{{ detailItem.reason }}</el-descriptions-item>
|
|
<el-descriptions-item label="原因">{{ detailItem.reason }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="审核状态">{{ detailItem.auditStatus }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="退款状态">{{ detailItem.refundStatus }}</el-descriptions-item>
|
|
|
|
|
|
|
+ <el-descriptions-item label="审核状态">
|
|
|
|
|
+ <el-tag :type="detailItem.auditStatus === '已通过' ? 'success' : detailItem.auditStatus === '已拒绝' ? 'danger' : 'warning'" size="small">{{ detailItem.auditStatus }}</el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="退款/处理状态">
|
|
|
|
|
+ <el-tag :type="detailItem.refundStatus === '已退款' || detailItem.refundStatus === '已补发' ? 'success' : 'info'" size="small">{{ detailItem.refundStatus }}</el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item v-if="detailItem.type === '退款'" label="可退额度">
|
|
|
|
|
+ {{ detailItem.amount === '$0.00' ? '-' : detailItem.amount + '(全额可退)' }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
<el-descriptions-item label="更新时间">{{ detailItem.updatedAt }}</el-descriptions-item>
|
|
<el-descriptions-item label="更新时间">{{ detailItem.updatedAt }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
</el-descriptions>
|
|
|
</template>
|
|
</template>
|
|
@@ -116,12 +184,25 @@ import { api } from '@/api/services';
|
|
|
import type { AfterSaleItem } from '@/types/page';
|
|
import type { AfterSaleItem } from '@/types/page';
|
|
|
|
|
|
|
|
const items = ref<AfterSaleItem[]>([]);
|
|
const items = ref<AfterSaleItem[]>([]);
|
|
|
|
|
+const loading = ref(false);
|
|
|
|
|
+const page = ref(1);
|
|
|
|
|
+const pageSize = ref(10);
|
|
|
const auditDialog = ref(false);
|
|
const auditDialog = ref(false);
|
|
|
const detailDrawer = ref(false);
|
|
const detailDrawer = ref(false);
|
|
|
const auditAction = ref<'approve' | 'reject'>('approve');
|
|
const auditAction = ref<'approve' | 'reject'>('approve');
|
|
|
const auditItem = ref<AfterSaleItem | null>(null);
|
|
const auditItem = ref<AfterSaleItem | null>(null);
|
|
|
const detailItem = ref<AfterSaleItem | null>(null);
|
|
const detailItem = ref<AfterSaleItem | null>(null);
|
|
|
const auditRemark = ref('');
|
|
const auditRemark = ref('');
|
|
|
|
|
+const returnDialog = ref(false);
|
|
|
|
|
+const returnCarrier = ref('');
|
|
|
|
|
+const returnTrackingNo = ref('');
|
|
|
|
|
+const returnTarget = ref<AfterSaleItem | null>(null);
|
|
|
|
|
+const resendDialog = ref(false);
|
|
|
|
|
+const resendWarehouse = ref('');
|
|
|
|
|
+const resendSku = ref('');
|
|
|
|
|
+const resendQty = ref(1);
|
|
|
|
|
+const resendRemark = ref('');
|
|
|
|
|
+const resendTarget = ref<AfterSaleItem | null>(null);
|
|
|
|
|
|
|
|
const filters = ref({ afterSaleNo: '', orderNo: '', type: '', auditStatus: '' });
|
|
const filters = ref({ afterSaleNo: '', orderNo: '', type: '', auditStatus: '' });
|
|
|
|
|
|
|
@@ -135,9 +216,16 @@ const filteredItems = computed(() => {
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+const total = computed(() => filteredItems.value.length);
|
|
|
|
|
+
|
|
|
const loadData = async () => {
|
|
const loadData = async () => {
|
|
|
- const res = await api.getAfterSales();
|
|
|
|
|
- items.value = res.items;
|
|
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await api.getAfterSales();
|
|
|
|
|
+ items.value = res.items;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const resetFilters = () => {
|
|
const resetFilters = () => {
|
|
@@ -170,6 +258,45 @@ const doRefund = async (row: AfterSaleItem) => {
|
|
|
loadData();
|
|
loadData();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const openReturnTracking = (row: AfterSaleItem) => {
|
|
|
|
|
+ returnTarget.value = row;
|
|
|
|
|
+ returnCarrier.value = '';
|
|
|
|
|
+ returnTrackingNo.value = '';
|
|
|
|
|
+ returnDialog.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const confirmReturnTracking = async () => {
|
|
|
|
|
+ if (!returnTrackingNo.value) { ElMessage.warning('请输入物流单号'); return; }
|
|
|
|
|
+ await api.updateAfterSale(returnTarget.value!.id, { refundStatus: '已退货待入库' } as Partial<AfterSaleItem>);
|
|
|
|
|
+ returnDialog.value = false;
|
|
|
|
|
+ ElMessage.success('退货物流已录入');
|
|
|
|
|
+ loadData();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const confirmReceipt = async (row: AfterSaleItem) => {
|
|
|
|
|
+ await ElMessageBox.confirm(`确认已收到 ${row.afterSaleNo} 的退货并入库?`);
|
|
|
|
|
+ await api.updateAfterSale(row.id, { refundStatus: '已退款' } as Partial<AfterSaleItem>);
|
|
|
|
|
+ ElMessage.success('已确认入库并完成退款');
|
|
|
|
|
+ loadData();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const openResend = (row: AfterSaleItem) => {
|
|
|
|
|
+ resendTarget.value = row;
|
|
|
|
|
+ resendWarehouse.value = '';
|
|
|
|
|
+ resendSku.value = '';
|
|
|
|
|
+ resendQty.value = 1;
|
|
|
|
|
+ resendRemark.value = '';
|
|
|
|
|
+ resendDialog.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const confirmResend = async () => {
|
|
|
|
|
+ if (!resendWarehouse.value || !resendSku.value) { ElMessage.warning('请选择仓库和SKU'); return; }
|
|
|
|
|
+ await api.updateAfterSale(resendTarget.value!.id, { refundStatus: '已补发' } as Partial<AfterSaleItem>);
|
|
|
|
|
+ resendDialog.value = false;
|
|
|
|
|
+ ElMessage.success('补发单已生成');
|
|
|
|
|
+ loadData();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const openDetail = (item: AfterSaleItem) => {
|
|
const openDetail = (item: AfterSaleItem) => {
|
|
|
detailItem.value = item;
|
|
detailItem.value = item;
|
|
|
detailDrawer.value = true;
|
|
detailDrawer.value = true;
|