first
This commit is contained in:
814
admin-web/src/pages/orders/index.vue
Normal file
814
admin-web/src/pages/orders/index.vue
Normal file
@@ -0,0 +1,814 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { adminApi, type AdminOrderDetail, type AdminOrderListItem, type AdminOrderWarehouseOption } from "../../api/admin";
|
||||
import OrderStatusTag from "../../components/OrderStatusTag.vue";
|
||||
|
||||
const loading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
const receiveSubmitting = ref(false);
|
||||
const returnReceiveSubmitting = ref(false);
|
||||
const warehouseSubmitting = ref(false);
|
||||
const warehouseDialogVisible = ref(false);
|
||||
const warehouseOptionsLoading = ref(false);
|
||||
const warehouseOptions = ref<AdminOrderWarehouseOption[]>([]);
|
||||
const selectedWarehouseId = ref(0);
|
||||
const returnDialogVisible = ref(false);
|
||||
const returnSubmitting = ref(false);
|
||||
const returnExpressCompany = ref("");
|
||||
const returnTrackingNo = ref("");
|
||||
|
||||
const keyword = ref("");
|
||||
const serviceProvider = ref("");
|
||||
const status = ref("");
|
||||
const sourceChannel = ref("");
|
||||
|
||||
const orders = ref<AdminOrderListItem[]>([]);
|
||||
const detail = ref<AdminOrderDetail | null>(null);
|
||||
|
||||
const providerOptions = [
|
||||
{ label: "全部服务", value: "" },
|
||||
{ label: "实物鉴定", value: "anxinyan" },
|
||||
{ label: "中检鉴定", value: "zhongjian" },
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{ label: "全部状态", value: "" },
|
||||
{ label: "待补资料", value: "pending_supplement" },
|
||||
{ label: "待寄送", value: "pending_shipping" },
|
||||
{ label: "鉴定中", value: "in_first_review" },
|
||||
{ label: "待寄回", value: "report_published" },
|
||||
{ label: "回寄途中", value: "returning" },
|
||||
{ label: "已完成签收", value: "completed_signed" },
|
||||
];
|
||||
|
||||
const sourceChannelOptions = [
|
||||
{ label: "全部渠道", value: "" },
|
||||
{ label: "小程序", value: "mini_program" },
|
||||
{ label: "H5", value: "h5" },
|
||||
{ label: "大客户推送订单", value: "enterprise_push" },
|
||||
];
|
||||
|
||||
const usageStatusMap: Record<string, string> = {
|
||||
new: "全新未使用",
|
||||
light_use: "轻微使用痕迹",
|
||||
used: "长期使用",
|
||||
};
|
||||
|
||||
const usageStatusText = computed(() => {
|
||||
const value = detail.value?.extra_info.usage_status || "";
|
||||
return value ? usageStatusMap[value] || value : "-";
|
||||
});
|
||||
|
||||
const productTitle = computed(() => {
|
||||
if (!detail.value) {
|
||||
return "待完善物品信息";
|
||||
}
|
||||
return detail.value.product_info.product_name || "待完善物品信息";
|
||||
});
|
||||
|
||||
const productMetaText = computed(() => {
|
||||
if (!detail.value) {
|
||||
return "物品信息待完善";
|
||||
}
|
||||
const parts = [
|
||||
detail.value.product_info.category_name,
|
||||
detail.value.product_info.brand_name,
|
||||
].filter(Boolean);
|
||||
|
||||
return parts.length ? parts.join(" / ") : "物品信息待完善";
|
||||
});
|
||||
|
||||
const canMarkReceived = computed(() => {
|
||||
if (!detail.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (detail.value.order_info.can_mark_received) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
detail.value.order_info.order_status === "pending_shipping" &&
|
||||
Boolean(detail.value.logistics_info?.tracking_no) &&
|
||||
detail.value.logistics_info?.tracking_status !== "received"
|
||||
);
|
||||
});
|
||||
|
||||
const logisticsActionText = computed(() => {
|
||||
if (!detail.value?.logistics_info) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return canMarkReceived.value ? "用户已提交/寄出,待鉴定中心签收" : detail.value.logistics_info.tracking_status_text;
|
||||
});
|
||||
|
||||
const canSubmitReturnLogistics = computed(() => Boolean(detail.value?.order_info.can_submit_return_logistics));
|
||||
const returnLogisticsBlockReason = computed(() => detail.value?.order_info.return_logistics_block_reason || "");
|
||||
const canMarkReturnReceived = computed(() => Boolean(detail.value?.order_info.can_mark_return_received));
|
||||
|
||||
async function fetchOrders() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await adminApi.getOrders({
|
||||
keyword: keyword.value,
|
||||
service_provider: serviceProvider.value,
|
||||
status: status.value,
|
||||
source_channel: sourceChannel.value,
|
||||
});
|
||||
orders.value = response.data.list;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("订单列表加载失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function openDetail(row: AdminOrderListItem) {
|
||||
detailLoading.value = true;
|
||||
drawerVisible.value = true;
|
||||
try {
|
||||
const response = await adminApi.getOrderDetail(row.id);
|
||||
detail.value = response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("订单详情加载失败");
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadDetail() {
|
||||
if (!detail.value) return;
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
const response = await adminApi.getOrderDetail(detail.value.order_info.id);
|
||||
detail.value = response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("订单详情刷新失败");
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function markReceived() {
|
||||
if (!detail.value) return;
|
||||
receiveSubmitting.value = true;
|
||||
try {
|
||||
const response = await adminApi.receiveOrderLogistics(detail.value.order_info.id);
|
||||
ElMessage.success(response.message || "已标记签收");
|
||||
await reloadDetail();
|
||||
await fetchOrders();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("标记签收失败");
|
||||
} finally {
|
||||
receiveSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function openWarehouseDialog() {
|
||||
if (!detail.value) return;
|
||||
warehouseOptionsLoading.value = true;
|
||||
warehouseDialogVisible.value = true;
|
||||
selectedWarehouseId.value = detail.value.shipping_target?.warehouse_id || 0;
|
||||
try {
|
||||
const response = await adminApi.getOrderWarehouseOptions(detail.value.order_info.id);
|
||||
warehouseOptions.value = response.data.list;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("仓库列表加载失败");
|
||||
} finally {
|
||||
warehouseOptionsLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function submitWarehouseReassign() {
|
||||
if (!detail.value || !selectedWarehouseId.value) {
|
||||
ElMessage.warning("请先选择一个目标仓库");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm("改派后,用户寄送页将展示新的收货仓库地址。确定继续吗?", "改派仓库", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确认改派",
|
||||
cancelButtonText: "取消",
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
warehouseSubmitting.value = true;
|
||||
try {
|
||||
const response = await adminApi.reassignOrderWarehouse(detail.value.order_info.id, selectedWarehouseId.value);
|
||||
ElMessage.success(response.message || "仓库已改派");
|
||||
warehouseDialogVisible.value = false;
|
||||
await reloadDetail();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("仓库改派失败");
|
||||
} finally {
|
||||
warehouseSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openReturnDialog() {
|
||||
if (!detail.value) return;
|
||||
if (!canSubmitReturnLogistics.value) {
|
||||
ElMessage.warning(returnLogisticsBlockReason.value || "当前订单暂不支持登记回寄运单");
|
||||
return;
|
||||
}
|
||||
returnExpressCompany.value = detail.value.return_logistics?.express_company || "";
|
||||
returnTrackingNo.value = detail.value.return_logistics?.tracking_no || "";
|
||||
returnDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function submitReturnLogistics() {
|
||||
if (!canSubmitReturnLogistics.value) {
|
||||
ElMessage.warning(returnLogisticsBlockReason.value || "当前订单暂不支持登记回寄运单");
|
||||
return;
|
||||
}
|
||||
if (!detail.value || !returnExpressCompany.value.trim() || !returnTrackingNo.value.trim()) {
|
||||
ElMessage.warning("请完整填写回寄快递公司和运单号");
|
||||
return;
|
||||
}
|
||||
|
||||
returnSubmitting.value = true;
|
||||
try {
|
||||
const response = await adminApi.saveOrderReturnLogistics({
|
||||
id: detail.value.order_info.id,
|
||||
express_company: returnExpressCompany.value.trim(),
|
||||
tracking_no: returnTrackingNo.value.trim(),
|
||||
});
|
||||
ElMessage.success(response.message || "回寄运单已登记");
|
||||
returnDialogVisible.value = false;
|
||||
await reloadDetail();
|
||||
await fetchOrders();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error(error instanceof Error ? error.message : "回寄运单登记失败");
|
||||
} finally {
|
||||
returnSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function markReturnReceived() {
|
||||
if (!detail.value) return;
|
||||
returnReceiveSubmitting.value = true;
|
||||
try {
|
||||
const response = await adminApi.receiveOrderReturnLogistics(detail.value.order_info.id);
|
||||
ElMessage.success(response.message || "已标记用户签收");
|
||||
await reloadDetail();
|
||||
await fetchOrders();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("标记用户签收失败");
|
||||
} finally {
|
||||
returnReceiveSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchOrders);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card class="panel-card" shadow="never">
|
||||
<div class="filters-row">
|
||||
<el-input v-model="keyword" placeholder="搜索订单号 / 鉴定单号 / 商品名称" clearable style="width: 320px" />
|
||||
<el-select v-model="serviceProvider" placeholder="服务类型" style="width: 160px">
|
||||
<el-option v-for="item in providerOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="status" placeholder="订单状态" style="width: 160px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="sourceChannel" placeholder="下单渠道" style="width: 170px">
|
||||
<el-option v-for="item in sourceChannelOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="fetchOrders">查询</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="panel-card orders-table" shadow="never">
|
||||
<el-table v-loading="loading" :data="orders" stripe>
|
||||
<el-table-column prop="order_no" label="订单号" min-width="170" />
|
||||
<el-table-column prop="appraisal_no" label="鉴定单号" min-width="180" />
|
||||
<el-table-column prop="product_name" label="商品名称" min-width="220" />
|
||||
<el-table-column prop="service_provider_text" label="服务类型" min-width="120" />
|
||||
<el-table-column label="下单渠道" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.source_channel_text }}</span>
|
||||
<div v-if="row.source_customer_id" class="table-subtext">客户ID:{{ row.source_customer_id }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单状态" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<OrderStatusTag :status="row.display_status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="estimated_finish_time" label="预计完成时间" min-width="170" />
|
||||
<el-table-column prop="pay_amount" label="金额" min-width="100">
|
||||
<template #default="{ row }">¥{{ row.pay_amount }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" min-width="170" />
|
||||
<el-table-column label="操作" fixed="right" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openDetail(row)">查看详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-drawer v-model="drawerVisible" size="68%" title="订单详情">
|
||||
<div v-loading="detailLoading" v-if="detail" class="order-detail-shell">
|
||||
<div class="detail-card order-detail-hero">
|
||||
<div class="order-detail-hero__main">
|
||||
<div class="order-detail-hero__eyebrow">订单履约工作区</div>
|
||||
<div class="order-detail-hero__title">{{ productTitle }}</div>
|
||||
<div class="order-detail-hero__meta">{{ productMetaText }}</div>
|
||||
</div>
|
||||
<div class="order-detail-hero__side">
|
||||
<div class="order-detail-hero__tags">
|
||||
<OrderStatusTag :status="detail.order_info.display_status" />
|
||||
<span class="order-detail-chip">{{ detail.order_info.service_provider_text }}</span>
|
||||
</div>
|
||||
<div class="order-detail-hero__actions">
|
||||
<el-button
|
||||
v-if="canMarkReceived"
|
||||
type="primary"
|
||||
:loading="receiveSubmitting"
|
||||
@click="markReceived"
|
||||
>
|
||||
标记鉴定中心签收
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="detail.order_info.can_reassign_warehouse"
|
||||
type="primary"
|
||||
plain
|
||||
:loading="warehouseOptionsLoading"
|
||||
@click="openWarehouseDialog"
|
||||
>
|
||||
手动改派仓库
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canSubmitReturnLogistics || returnLogisticsBlockReason"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="!canSubmitReturnLogistics"
|
||||
@click="openReturnDialog"
|
||||
>
|
||||
{{ detail.return_logistics?.tracking_no ? '更新回寄运单' : '登记回寄运单' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canMarkReturnReceived"
|
||||
type="primary"
|
||||
:loading="returnReceiveSubmitting"
|
||||
@click="markReturnReceived"
|
||||
>
|
||||
标记用户签收
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-grid">
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">订单概览</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">订单号</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.order_no }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">鉴定单号</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.appraisal_no }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">服务类型</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.service_provider_text }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">下单渠道</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.source_channel_text || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item" v-if="detail.order_info.source_customer_id">
|
||||
<div class="order-detail-item__label">大客户客户 ID</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.source_customer_id }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">当前状态</div>
|
||||
<div class="order-detail-item__value"><OrderStatusTag :status="detail.order_info.display_status" /></div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">订单金额</div>
|
||||
<div class="order-detail-item__value">¥{{ detail.order_info.pay_amount }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">预计完成</div>
|
||||
<div class="order-detail-item__value">{{ detail.order_info.estimated_finish_time || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">商品信息</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">商品名称</div>
|
||||
<div class="order-detail-item__value">{{ detail.product_info.product_name || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">品类 / 品牌</div>
|
||||
<div class="order-detail-item__value">{{ detail.product_info.category_name || "-" }} / {{ detail.product_info.brand_name || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">颜色 / 规格</div>
|
||||
<div class="order-detail-item__value">{{ detail.product_info.color || "-" }} / {{ detail.product_info.size_spec || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">补充信息</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">购买渠道</div>
|
||||
<div class="order-detail-item__value">{{ detail.extra_info.purchase_channel || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">购买价格</div>
|
||||
<div class="order-detail-item__value">¥{{ detail.extra_info.purchase_price }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">使用情况</div>
|
||||
<div class="order-detail-item__value">{{ usageStatusText }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">补充说明</div>
|
||||
<div class="order-detail-item__value">{{ detail.extra_info.condition_desc || detail.extra_info.remark || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.shipping_target">
|
||||
<div class="detail-card__title">收货仓库</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">仓库名称 / 编码</div>
|
||||
<div class="order-detail-item__value">{{ detail.shipping_target.warehouse_name }} / {{ detail.shipping_target.warehouse_code }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">收件人 / 联系电话</div>
|
||||
<div class="order-detail-item__value">{{ detail.shipping_target.receiver_name }} / {{ detail.shipping_target.receiver_mobile }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">收件地址</div>
|
||||
<div class="order-detail-item__value">{{ detail.shipping_target.full_address }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">服务时间</div>
|
||||
<div class="order-detail-item__value">{{ detail.shipping_target.service_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="detail-card__title">寄回地址</div>
|
||||
<div v-if="detail.return_address" class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">收件人 / 联系电话</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_address.consignee }} / {{ detail.return_address.mobile }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">寄回地址</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_address.full_address }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="用户暂未确认寄回地址" :image-size="64" />
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.logistics_info">
|
||||
<div class="detail-card__title">物流信息</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">快递公司 / 运单号</div>
|
||||
<div class="order-detail-item__value">{{ detail.logistics_info.express_company || "-" }} / {{ detail.logistics_info.tracking_no || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">物流状态</div>
|
||||
<div class="order-detail-item__value">{{ logisticsActionText }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">最新节点</div>
|
||||
<div class="order-detail-item__value">{{ detail.logistics_info.latest_desc || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full" v-if="detail.logistics_info.latest_time">
|
||||
<div class="order-detail-item__label">最新更新时间</div>
|
||||
<div class="order-detail-item__value">{{ detail.logistics_info.latest_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="canMarkReceived" class="detail-card__desc" style="margin-top: 16px;">
|
||||
<el-alert title="待签收操作" description="物流信息已提交,确认鉴定中心实际收货后再执行签收。" type="warning" :closable="false" show-icon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.return_logistics">
|
||||
<div class="detail-card__title">回寄物流</div>
|
||||
<div class="order-detail-grid">
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">快递公司 / 运单号</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_logistics.express_company || "-" }} / {{ detail.return_logistics.tracking_no || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item">
|
||||
<div class="order-detail-item__label">物流状态</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_logistics.tracking_status_text }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full">
|
||||
<div class="order-detail-item__label">最新节点</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_logistics.latest_desc || "-" }}</div>
|
||||
</div>
|
||||
<div class="order-detail-item order-detail-item--full" v-if="detail.return_logistics.latest_time">
|
||||
<div class="order-detail-item__label">最新更新时间</div>
|
||||
<div class="order-detail-item__value">{{ detail.return_logistics.latest_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.report_summary">
|
||||
<div class="detail-card__title">报告信息</div>
|
||||
<el-alert
|
||||
v-if="returnLogisticsBlockReason"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
:title="returnLogisticsBlockReason"
|
||||
description="请先在报告中心发布订单报告,发布后再登记回寄运单。"
|
||||
style="margin-top: 12px;"
|
||||
/>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告编号</div>
|
||||
<div class="detail-value">{{ detail.report_summary.report_no }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">报告标题</div>
|
||||
<div class="detail-value">{{ detail.report_summary.report_title }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">发布时间</div>
|
||||
<div class="detail-value">{{ detail.report_summary.publish_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">时间轴</div>
|
||||
<div class="timeline-list" style="margin-top: 14px">
|
||||
<div v-for="item in detail.timeline" :key="`${item.node_text}-${item.occurred_at}`" class="timeline-node">
|
||||
<div class="timeline-node__title">{{ item.node_text }}</div>
|
||||
<div class="timeline-node__time">{{ item.occurred_at }}</div>
|
||||
<div class="timeline-node__desc">{{ item.node_desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.logistics_info" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">物流轨迹</div>
|
||||
<div class="timeline-list" style="margin-top: 14px">
|
||||
<div v-for="item in detail.logistics_info.nodes" :key="`${item.node_time}-${item.node_desc}`" class="timeline-node">
|
||||
<div class="timeline-node__title">{{ item.node_desc }}</div>
|
||||
<div class="timeline-node__time">{{ item.node_time }}</div>
|
||||
<div class="timeline-node__desc">{{ item.node_location || "-" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card" v-if="detail.supplement_task" style="grid-column: 1 / -1">
|
||||
<div class="detail-card__title">补图任务</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">补图原因</div>
|
||||
<div class="detail-value">{{ detail.supplement_task.reason }}</div>
|
||||
</div>
|
||||
<div class="detail-card__desc">
|
||||
<div class="detail-label">截止时间</div>
|
||||
<div class="detail-value">{{ detail.supplement_task.deadline }}</div>
|
||||
</div>
|
||||
<div class="timeline-list" style="margin-top: 14px">
|
||||
<div v-for="item in detail.supplement_task.items" :key="item.item_name" class="timeline-node">
|
||||
<div class="timeline-node__title">{{ item.item_name }}</div>
|
||||
<div class="timeline-node__desc">{{ item.guide_text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog v-model="warehouseDialogVisible" title="改派收货仓库" width="720px">
|
||||
<div v-loading="warehouseOptionsLoading" style="display: grid; gap: 14px;">
|
||||
<div
|
||||
v-for="item in warehouseOptions"
|
||||
:key="item.id"
|
||||
:style="{
|
||||
border: selectedWarehouseId === item.id ? '1px solid #c8a45d' : '1px solid var(--admin-border)',
|
||||
borderRadius: '14px',
|
||||
padding: '16px 18px',
|
||||
cursor: 'pointer',
|
||||
background: selectedWarehouseId === item.id ? 'rgba(200, 164, 93, 0.08)' : '#fff',
|
||||
}"
|
||||
@click="selectedWarehouseId = item.id"
|
||||
>
|
||||
<div style="display:flex; justify-content:space-between; gap: 16px; align-items:center;">
|
||||
<div style="font-weight:700;">{{ item.warehouse_name }}</div>
|
||||
<div style="color: var(--admin-text-subtle);">{{ item.is_default ? '默认仓库' : '可选仓库' }}</div>
|
||||
</div>
|
||||
<div style="margin-top: 8px; color: var(--admin-text-subtle);">{{ item.service_provider_text }} / {{ item.warehouse_code }}</div>
|
||||
<div style="margin-top: 8px;">{{ item.receiver_name }} / {{ item.receiver_mobile }}</div>
|
||||
<div style="margin-top: 8px;">{{ item.full_address }}</div>
|
||||
<div style="margin-top: 8px; color: var(--admin-text-subtle);">{{ item.service_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="warehouseDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="warehouseSubmitting" @click="submitWarehouseReassign">确认改派</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="returnDialogVisible" title="登记回寄运单" width="520px">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="回寄快递公司">
|
||||
<el-input v-model="returnExpressCompany" placeholder="例如:顺丰速运" />
|
||||
</el-form-item>
|
||||
<el-form-item label="回寄运单号">
|
||||
<el-input v-model="returnTrackingNo" placeholder="请输入回寄运单号" />
|
||||
</el-form-item>
|
||||
<el-alert
|
||||
v-if="detail?.return_address"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="当前寄回地址"
|
||||
:description="`${detail.return_address.consignee} / ${detail.return_address.mobile} / ${detail.return_address.full_address}`"
|
||||
/>
|
||||
<el-alert
|
||||
v-else
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="用户尚未确认寄回地址"
|
||||
description="请先提醒用户在订单详情中确认寄回地址,再登记回寄运单。"
|
||||
/>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="returnDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="returnSubmitting" :disabled="!canSubmitReturnLogistics" @click="submitReturnLogistics">确认登记</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.order-detail-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.order-detail-hero {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding: 24px;
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(200, 164, 93, 0.12), transparent 30%),
|
||||
linear-gradient(135deg, #fffdfa 0%, #fbf8f1 100%);
|
||||
}
|
||||
|
||||
.order-detail-hero__main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.order-detail-hero__eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(200, 164, 93, 0.12);
|
||||
color: #7a5a21;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.order-detail-hero__title {
|
||||
margin-top: 14px;
|
||||
color: var(--admin-text-main);
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.order-detail-hero__meta {
|
||||
margin-top: 10px;
|
||||
color: var(--admin-text-subtle);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.order-detail-hero__side {
|
||||
min-width: 260px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.order-detail-hero__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.order-detail-hero__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.order-detail-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(72, 104, 133, 0.1);
|
||||
color: var(--admin-progress);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.table-subtext {
|
||||
margin-top: 4px;
|
||||
color: var(--admin-text-subtle);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.order-detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.order-detail-item {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #efe8d9;
|
||||
border-radius: 16px;
|
||||
background: #fcfaf5;
|
||||
}
|
||||
|
||||
.order-detail-item--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.order-detail-item__label {
|
||||
color: var(--admin-text-subtle);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.order-detail-item__value {
|
||||
margin-top: 8px;
|
||||
color: var(--admin-text-main);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.order-detail-hero {
|
||||
grid-template-columns: 1fr;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.order-detail-hero__side {
|
||||
min-width: 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.order-detail-hero__tags,
|
||||
.order-detail-hero__actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.order-detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user