1306 lines
47 KiB
Vue
1306 lines
47 KiB
Vue
<script setup lang="ts">
|
||
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||
import { ElMessage, ElMessageBox } from "element-plus";
|
||
import {
|
||
adminApi,
|
||
type AdminExpressCompanyItem,
|
||
type AdminExpressCompanyRecognitionCandidate,
|
||
type AdminManualOrderCreatePayload,
|
||
type AdminManualOrderMeta,
|
||
type AdminOrderDetail,
|
||
type AdminOrderListItem,
|
||
type AdminOrderWarehouseOption,
|
||
type AdminServicePricePackage,
|
||
} from "../../api/admin";
|
||
import OrderStatusTag from "../../components/OrderStatusTag.vue";
|
||
import { recognizeReturnAddress } from "../../utils/address-recognition";
|
||
|
||
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 returnRecognitionLoading = ref(false);
|
||
const returnRecognitionCandidates = ref<AdminExpressCompanyRecognitionCandidate[]>([]);
|
||
const expressCompanyLoading = ref(false);
|
||
const expressCompanyOptions = ref<AdminExpressCompanyItem[]>([]);
|
||
const defaultExpressCompany = ref("");
|
||
const manualDialogVisible = ref(false);
|
||
const manualSubmitting = ref(false);
|
||
const manualMetaLoading = ref(false);
|
||
const manualMeta = ref<AdminManualOrderMeta>({ categories: [], brands: [], service_price_packages: [] });
|
||
const manualForm = ref<AdminManualOrderCreatePayload>(createManualOrderForm());
|
||
const manualAddressRecognitionText = 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" },
|
||
{ label: "后台补录订单", value: "manual_entry" },
|
||
];
|
||
|
||
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));
|
||
const expressCompanySelectOptions = computed(() => {
|
||
if (!returnExpressCompany.value || expressCompanyOptions.value.some((item) => item.company_name === returnExpressCompany.value)) {
|
||
return expressCompanyOptions.value;
|
||
}
|
||
|
||
return [
|
||
{
|
||
id: 0,
|
||
company_name: returnExpressCompany.value,
|
||
company_code: returnExpressCompany.value,
|
||
status: "enabled",
|
||
status_text: "启用中",
|
||
is_default: false,
|
||
sort_order: 0,
|
||
remark: "",
|
||
created_at: "",
|
||
updated_at: "",
|
||
},
|
||
...expressCompanyOptions.value,
|
||
];
|
||
});
|
||
const manualServicePackages = computed<AdminServicePricePackage[]>(() =>
|
||
manualMeta.value.service_price_packages.find((item) => item.service_provider === manualForm.value.service_provider)?.packages.filter((item) => item.is_enabled) || [],
|
||
);
|
||
let returnRecognitionTimer: ReturnType<typeof setTimeout> | undefined;
|
||
|
||
function createManualOrderForm(): AdminManualOrderCreatePayload {
|
||
return {
|
||
service_provider: "anxinyan",
|
||
price_package_id: 0,
|
||
price_package_code: "",
|
||
product_info: {
|
||
category_id: 0,
|
||
brand_id: 0,
|
||
brand_name: "",
|
||
product_name: "",
|
||
color: "",
|
||
size_spec: "",
|
||
serial_no: "",
|
||
},
|
||
extra_info: {
|
||
purchase_channel: "",
|
||
purchase_price: 0,
|
||
usage_status: "",
|
||
condition_desc: "",
|
||
remark: "",
|
||
},
|
||
return_address: {
|
||
consignee: "",
|
||
mobile: "",
|
||
province: "",
|
||
city: "",
|
||
district: "",
|
||
detail_address: "",
|
||
},
|
||
materials: [
|
||
{
|
||
item_code: "manual_initial",
|
||
item_name: "补录资料",
|
||
is_required: false,
|
||
files: [],
|
||
},
|
||
],
|
||
};
|
||
}
|
||
|
||
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 ensureManualMeta() {
|
||
if (manualMeta.value.categories.length) return;
|
||
manualMetaLoading.value = true;
|
||
try {
|
||
const response = await adminApi.getManualOrderMeta();
|
||
manualMeta.value = response.data;
|
||
applyManualDefaultPackage(true);
|
||
} catch (error) {
|
||
console.error(error);
|
||
ElMessage.error("补录订单选项加载失败");
|
||
} finally {
|
||
manualMetaLoading.value = false;
|
||
}
|
||
}
|
||
|
||
function applyManualDefaultPackage(force = false) {
|
||
const current = manualServicePackages.value.find((item) => item.id === manualForm.value.price_package_id);
|
||
if (current && !force) {
|
||
manualForm.value.price_package_code = current.package_code;
|
||
return;
|
||
}
|
||
|
||
const target = manualServicePackages.value.find((item) => item.is_default) || manualServicePackages.value[0];
|
||
manualForm.value.price_package_id = target?.id || 0;
|
||
manualForm.value.price_package_code = target?.package_code || "";
|
||
}
|
||
|
||
async function openManualDialog() {
|
||
manualForm.value = createManualOrderForm();
|
||
manualAddressRecognitionText.value = "";
|
||
manualDialogVisible.value = true;
|
||
await ensureManualMeta();
|
||
applyManualDefaultPackage(true);
|
||
}
|
||
|
||
async function ensureExpressCompanyOptions() {
|
||
if (expressCompanyOptions.value.length) return;
|
||
expressCompanyLoading.value = true;
|
||
try {
|
||
const response = await adminApi.getExpressCompanies({ enabled_only: 1 });
|
||
expressCompanyOptions.value = response.data.list;
|
||
defaultExpressCompany.value = response.data.default_company;
|
||
} catch (error) {
|
||
console.error(error);
|
||
ElMessage.error("快递公司列表加载失败");
|
||
} finally {
|
||
expressCompanyLoading.value = false;
|
||
}
|
||
}
|
||
|
||
function applyRecognizedManualAddress() {
|
||
const result = recognizeReturnAddress(manualAddressRecognitionText.value);
|
||
if (!result.ok || !result.address) {
|
||
ElMessage.warning(result.message || "寄回地址识别失败");
|
||
return;
|
||
}
|
||
|
||
manualForm.value.return_address = {
|
||
...manualForm.value.return_address,
|
||
...result.address,
|
||
};
|
||
ElMessage.success("寄回地址已识别并填入");
|
||
}
|
||
|
||
function validateManualForm() {
|
||
const form = manualForm.value;
|
||
if (!form.price_package_id) {
|
||
ElMessage.warning("请选择价格套餐");
|
||
return false;
|
||
}
|
||
if (!form.product_info.category_id) {
|
||
ElMessage.warning("请选择品类");
|
||
return false;
|
||
}
|
||
const address = form.return_address;
|
||
if (!address.consignee.trim() || !address.mobile.trim() || !address.province.trim() || !address.city.trim() || !address.district.trim() || !address.detail_address.trim()) {
|
||
ElMessage.warning("请完整填写寄回收件信息");
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
async function submitManualOrder() {
|
||
if (!validateManualForm()) return;
|
||
manualSubmitting.value = true;
|
||
try {
|
||
const payload: AdminManualOrderCreatePayload = JSON.parse(JSON.stringify(manualForm.value));
|
||
payload.product_info.brand_id = 0;
|
||
payload.product_info.brand_name = payload.product_info.brand_name.trim();
|
||
const response = await adminApi.createManualOrder(payload);
|
||
ElMessage.success(`补录订单已创建:${response.data.order_no} / ${response.data.appraisal_no}`);
|
||
manualDialogVisible.value = false;
|
||
await fetchOrders();
|
||
} catch (error) {
|
||
console.error(error);
|
||
ElMessage.error(error instanceof Error ? error.message : "补录订单创建失败");
|
||
} finally {
|
||
manualSubmitting.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;
|
||
}
|
||
}
|
||
|
||
async function openReturnDialog() {
|
||
if (!detail.value) return;
|
||
if (!canSubmitReturnLogistics.value) {
|
||
ElMessage.warning(returnLogisticsBlockReason.value || "当前订单暂不支持登记回寄运单");
|
||
return;
|
||
}
|
||
await ensureExpressCompanyOptions();
|
||
returnExpressCompany.value = detail.value.return_logistics?.express_company || defaultExpressCompany.value || expressCompanyOptions.value[0]?.company_name || "";
|
||
returnTrackingNo.value = detail.value.return_logistics?.tracking_no || "";
|
||
returnRecognitionCandidates.value = [];
|
||
returnDialogVisible.value = true;
|
||
if (returnTrackingNo.value) {
|
||
scheduleReturnRecognition();
|
||
}
|
||
}
|
||
|
||
async function recognizeReturnExpressCompany() {
|
||
const trackingNo = returnTrackingNo.value.trim();
|
||
if (!returnDialogVisible.value || !trackingNo) {
|
||
returnRecognitionCandidates.value = [];
|
||
return;
|
||
}
|
||
|
||
returnRecognitionLoading.value = true;
|
||
try {
|
||
const response = await adminApi.recognizeExpressCompany({
|
||
tracking_no: trackingNo,
|
||
company_name: returnExpressCompany.value.trim(),
|
||
});
|
||
const result = response.data;
|
||
returnRecognitionCandidates.value = result.candidates || [];
|
||
if (result.resolved) {
|
||
returnExpressCompany.value = result.resolved.company_name;
|
||
} else if (result.candidates.length === 1) {
|
||
returnExpressCompany.value = result.candidates[0].company_name;
|
||
}
|
||
} catch (error) {
|
||
console.error(error);
|
||
returnRecognitionCandidates.value = [];
|
||
} finally {
|
||
returnRecognitionLoading.value = false;
|
||
}
|
||
}
|
||
|
||
function scheduleReturnRecognition() {
|
||
if (returnRecognitionTimer) {
|
||
clearTimeout(returnRecognitionTimer);
|
||
}
|
||
returnRecognitionTimer = setTimeout(() => {
|
||
void recognizeReturnExpressCompany();
|
||
}, 500);
|
||
}
|
||
|
||
function chooseReturnRecognitionCandidate(candidate: AdminExpressCompanyRecognitionCandidate) {
|
||
returnExpressCompany.value = candidate.company_name;
|
||
returnRecognitionCandidates.value = [candidate];
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
watch(returnTrackingNo, () => {
|
||
if (returnDialogVisible.value) {
|
||
scheduleReturnRecognition();
|
||
}
|
||
});
|
||
|
||
watch(() => manualForm.value.service_provider, () => {
|
||
applyManualDefaultPackage(true);
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
if (returnRecognitionTimer) {
|
||
clearTimeout(returnRecognitionTimer);
|
||
}
|
||
});
|
||
|
||
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>
|
||
<el-button @click="openManualDialog">补录订单</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 prop="price_package_name" label="价格套餐" min-width="150">
|
||
<template #default="{ row }">{{ row.price_package_name || "-" }}</template>
|
||
</el-table-column>
|
||
<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.price_package_name || "-" }}</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">
|
||
<div class="order-detail-item__label">快递100状态</div>
|
||
<div class="order-detail-item__value">{{ detail.logistics_info.provider_status_text || detail.logistics_info.sync_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.logistics_info.latest_desc || "-" }}</div>
|
||
</div>
|
||
<div class="order-detail-item order-detail-item--full" v-if="detail.logistics_info.sync_error">
|
||
<div class="order-detail-item__label">同步异常</div>
|
||
<div class="order-detail-item__value">{{ detail.logistics_info.sync_error }}</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">
|
||
<div class="order-detail-item__label">快递100状态</div>
|
||
<div class="order-detail-item__value">{{ detail.return_logistics.provider_status_text || detail.return_logistics.sync_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.sync_error">
|
||
<div class="order-detail-item__label">同步异常</div>
|
||
<div class="order-detail-item__value">{{ detail.return_logistics.sync_error }}</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.return_logistics" style="grid-column: 1 / -1">
|
||
<div class="detail-card__title">回寄物流轨迹</div>
|
||
<div v-if="detail.return_logistics.nodes.length" class="timeline-list" style="margin-top: 14px">
|
||
<div v-for="item in detail.return_logistics.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>
|
||
<el-empty v-else description="暂无回寄物流轨迹" :image-size="64" />
|
||
</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-select
|
||
v-model="returnExpressCompany"
|
||
:loading="expressCompanyLoading"
|
||
filterable
|
||
placeholder="请选择回寄快递公司"
|
||
style="width: 100%"
|
||
>
|
||
<el-option
|
||
v-for="item in expressCompanySelectOptions"
|
||
:key="`${item.id}-${item.company_name}`"
|
||
:label="item.company_name"
|
||
:value="item.company_name"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="回寄运单号">
|
||
<el-input v-model="returnTrackingNo" placeholder="请输入回寄运单号" />
|
||
</el-form-item>
|
||
<div v-if="returnRecognitionLoading" style="margin: -8px 0 12px; color: var(--admin-text-subtle); font-size: 12px;">
|
||
正在识别快递公司...
|
||
</div>
|
||
<div v-if="returnRecognitionCandidates.length" style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px;">
|
||
<el-tag
|
||
v-for="candidate in returnRecognitionCandidates"
|
||
:key="`${candidate.company_code}-${candidate.company_name}`"
|
||
effect="plain"
|
||
style="cursor: pointer;"
|
||
@click="chooseReturnRecognitionCandidate(candidate)"
|
||
>
|
||
{{ candidate.company_name }}
|
||
</el-tag>
|
||
</div>
|
||
<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>
|
||
|
||
<el-dialog v-model="manualDialogVisible" title="补录订单" width="860px" destroy-on-close>
|
||
<div v-loading="manualMetaLoading" class="manual-order-form">
|
||
<el-alert
|
||
type="info"
|
||
:closable="false"
|
||
show-icon
|
||
title="补录订单创建后为待入库状态"
|
||
description="创建成功后,可在入库台使用订单号或鉴定单号匹配并绑定内部流转挂牌,不需要填写寄入快递单号。"
|
||
/>
|
||
|
||
<div class="manual-section">
|
||
<div class="manual-section__title">订单与商品</div>
|
||
<el-form label-position="top">
|
||
<div class="manual-grid">
|
||
<el-form-item label="服务类型">
|
||
<el-select v-model="manualForm.service_provider" style="width: 100%">
|
||
<el-option label="实物鉴定" value="anxinyan" />
|
||
<el-option label="中检鉴定" value="zhongjian" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="价格套餐">
|
||
<el-select
|
||
v-model="manualForm.price_package_id"
|
||
style="width: 100%"
|
||
:disabled="manualServicePackages.length === 0"
|
||
@change="applyManualDefaultPackage(false)"
|
||
>
|
||
<el-option
|
||
v-for="item in manualServicePackages"
|
||
:key="item.id"
|
||
:label="`${item.package_name} / ¥${item.price}`"
|
||
:value="item.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="品类">
|
||
<el-select v-model="manualForm.product_info.category_id" filterable style="width: 100%">
|
||
<el-option v-for="item in manualMeta.categories" :key="item.id" :label="item.name" :value="item.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="品牌(选填)">
|
||
<el-input v-model="manualForm.product_info.brand_name" maxlength="128" placeholder="请输入品牌名称,可不填" />
|
||
</el-form-item>
|
||
<el-form-item label="商品名称">
|
||
<el-input v-model="manualForm.product_info.product_name" placeholder="可选,例如:Classic Flap 手袋" />
|
||
</el-form-item>
|
||
<el-form-item label="颜色">
|
||
<el-input v-model="manualForm.product_info.color" placeholder="可选" />
|
||
</el-form-item>
|
||
<el-form-item label="规格 / 尺寸">
|
||
<el-input v-model="manualForm.product_info.size_spec" placeholder="可选" />
|
||
</el-form-item>
|
||
<el-form-item label="序列号">
|
||
<el-input v-model="manualForm.product_info.serial_no" placeholder="可选" />
|
||
</el-form-item>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
|
||
<div class="manual-section">
|
||
<div class="manual-section__title">寄回信息</div>
|
||
<el-form label-position="top">
|
||
<el-form-item label="自动识别寄回地址">
|
||
<div class="manual-address-recognition">
|
||
<el-input
|
||
v-model="manualAddressRecognitionText"
|
||
type="textarea"
|
||
:rows="5"
|
||
resize="none"
|
||
placeholder="粘贴收货人、收货电话、收货地址,自动识别后填入下方字段"
|
||
/>
|
||
<el-button @click="applyRecognizedManualAddress">识别并填入</el-button>
|
||
</div>
|
||
</el-form-item>
|
||
<div class="manual-grid">
|
||
<el-form-item label="收件人">
|
||
<el-input v-model="manualForm.return_address.consignee" placeholder="用于匹配或创建用户" />
|
||
</el-form-item>
|
||
<el-form-item label="手机号">
|
||
<el-input v-model="manualForm.return_address.mobile" placeholder="按手机号复用已有用户" />
|
||
</el-form-item>
|
||
<el-form-item label="省份">
|
||
<el-input v-model="manualForm.return_address.province" placeholder="例如:广东省" />
|
||
</el-form-item>
|
||
<el-form-item label="城市">
|
||
<el-input v-model="manualForm.return_address.city" placeholder="例如:深圳市" />
|
||
</el-form-item>
|
||
<el-form-item label="区县">
|
||
<el-input v-model="manualForm.return_address.district" placeholder="例如:南山区" />
|
||
</el-form-item>
|
||
<el-form-item label="详细地址">
|
||
<el-input v-model="manualForm.return_address.detail_address" placeholder="街道、门牌号" />
|
||
</el-form-item>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="manualDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" :loading="manualSubmitting" :disabled="manualMetaLoading" @click="submitManualOrder">创建补录订单</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;
|
||
}
|
||
|
||
.manual-order-form {
|
||
display: grid;
|
||
gap: 18px;
|
||
}
|
||
|
||
.manual-section {
|
||
display: grid;
|
||
gap: 14px;
|
||
}
|
||
|
||
.manual-section__title {
|
||
color: var(--admin-text-main);
|
||
font-size: 16px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.manual-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 0 18px;
|
||
}
|
||
|
||
.manual-address-recognition {
|
||
display: grid;
|
||
gap: 10px;
|
||
}
|
||
|
||
.manual-address-recognition .el-button {
|
||
justify-self: flex-start;
|
||
}
|
||
|
||
.manual-upload-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
}
|
||
|
||
.manual-upload-hint {
|
||
color: var(--admin-text-subtle);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.manual-file-list {
|
||
display: grid;
|
||
gap: 10px;
|
||
}
|
||
|
||
.manual-file-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
min-width: 0;
|
||
padding: 10px 12px;
|
||
border: 1px solid var(--admin-border);
|
||
border-radius: 8px;
|
||
background: #fffdfa;
|
||
}
|
||
|
||
.manual-file-item span {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--admin-text-main);
|
||
font-size: 13px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
@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;
|
||
}
|
||
|
||
.manual-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|