refactor(site-settings): 统一站点配置管理并清理废弃页面

- 新增 useSiteSettings composable 替代各端硬编码的配置引用
- 移除 memberRecycle 和 fictitiousOrderList 废弃页面并增加缓存过滤
- 版权信息改为通过接口动态获取,后台新增版权归属配置字段
- 全局禁止 textarea 拖拽缩放并统一三端样式规则
- 会员列表增加状态 tab 筛选及启用/禁用双向切换功能
- 删除未使用的 getSpecificationList API 方法
This commit is contained in:
pikachu1995@126.com
2026-11-03 09:14:05 +08:00
parent 22f3e9b5e9
commit fb99824a3a
24 changed files with 422 additions and 1274 deletions

View File

@@ -1,235 +0,0 @@
<template>
<div class="search">
<el-card>
<el-form
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="订单号" prop="orderSn">
<el-input
v-model="searchForm.orderSn"
placeholder="请输入订单号"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="会员名称" prop="buyerName">
<el-input
v-model="searchForm.buyerName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="下单时间">
<el-date-picker
v-model="selectDate"
type="datetimerange"
value-format="YYYY-MM-DD"
clearable
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择起始时间"
style="width: 240px"
@change="selectDateRange"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<div class="order-tab">
<el-tabs v-model="currentStatus" @tab-click="onStatusTabClick">
<el-tab-pane
v-for="(item, index) in orderStatus"
:key="index"
:label="item.title"
:name="item.value"
/>
</el-tabs>
</div>
<el-table
ref="tableRef"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
@sort-change="changeSort"
>
<el-table-column prop="sn" label="订单号" min-width="230" show-overflow-tooltip />
<el-table-column prop="createTime" label="下单时间" width="200" />
<el-table-column label="订单来源" width="95">
<template #default="{ row }">
<span v-if="row">
<span v-if="row.clientType == 'H5'">移动端</span>
<span v-else-if="row.clientType == 'PC'">PC端</span>
<span v-else-if="row.clientType == 'WECHAT_MP'">小程序端</span>
<span v-else-if="row.clientType == 'APP'">移动应用端</span>
<span v-else>{{ row.clientType }}</span>
</span>
</template>
</el-table-column>
<el-table-column prop="memberName" label="买家名称" width="130" />
<el-table-column label="订单金额" prop="flowPrice" min-width="120" sortable="custom">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.flowPrice" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column label="订单状态" width="95">
<template #default="{ row }">
<template v-if="row">
<el-tag v-if="row.orderStatus == 'UNPAID'" type="danger">未付款</el-tag>
<el-tag v-else-if="row.orderStatus == 'PAID'" type="primary">已付款</el-tag>
<el-tag v-else-if="row.orderStatus == 'COMPLETED'" type="success">已完成</el-tag>
<el-tag v-else-if="row.orderStatus == 'TAKE'" type="warning">待核验</el-tag>
<el-tag v-else-if="row.orderStatus == 'CANCELLED'" type="info">已关闭</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<template v-if="row">
<a
class="link-text"
:class="{ disabled: row.orderStatus != 'UNPAID' }"
@click="row.orderStatus == 'UNPAID' && confirmPrice(row)"
>收款</a>
<span class="op-split">|</span>
<a class="link-text" @click="detail(row)">查看</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
import * as API_Order from "@/api/order";
import { ElMessage, ElMessageBox } from "element-plus";
import { customRouterPush } from "@/utils/filters";
defineOptions({ name: "fictitiousOrderList" });
const searchFormRef = ref(null);
const tableRef = ref(null);
const loading = ref(true);
const searchForm = reactive({
pageNumber: 1,
pageSize: 20,
sort: "",
order: "",
startDate: "",
endDate: "",
orderType: "VIRTUAL",
orderSn: "",
buyerName: "",
orderStatus: "",
});
const selectDate = ref(null);
const data = ref([]);
const total = ref(0);
const orderStatus = ref([
{ title: "全部", value: "" },
{ title: "未付款", value: "UNPAID" },
{ title: "已付款", value: "PAID" },
{ title: "待核验", value: "TAKE" },
{ title: "已完成", value: "COMPLETED" },
{ title: "已关闭", value: "CANCELLED" },
]);
const currentStatus = ref("");
onMounted(() => {
init();
});
function init() {
getDataList();
}
function changePage() {
getDataList();
}
function changePageSize() {
searchForm.pageNumber = 1;
getDataList();
}
function handleSearch() {
searchForm.pageNumber = 1;
searchForm.pageSize = 20;
getDataList();
}
function changeSort(e) {
searchForm.sort = e.prop;
searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
getDataList();
}
function selectDateRange(v) {
if (v) {
searchForm.startDate = v[0];
searchForm.endDate = v[1];
}
}
function getDataList() {
loading.value = true;
API_Order.getOrderList(searchForm).then((res) => {
loading.value = false;
if (res.success) {
data.value = res.result.records;
total.value = res.result.total;
}
});
}
function confirmPrice(v) {
ElMessageBox.confirm("您确定要收款吗?", "确认收款", { type: "warning" }).then(() => {
API_Order.orderPay(v.sn).then((res) => {
if (res.success) {
ElMessage.success("收款成功");
getDataList();
}
});
}).catch(() => {});
}
function detail(v) {
let sn = v.sn;
customRouterPush({
name: "order-detail",
query: { sn: sn, orderType: v.orderType },
});
}
function onStatusTabClick(tab) {
const item = tab.paneName;
currentStatus.value = item;
searchForm.orderStatus = item;
getDataList();
}
</script>
<style lang="scss" scoped>
.order-tab {
margin-top: 20px;
}
</style>

View File

@@ -1,18 +1,20 @@
<template>
<div class="order-detail-page">
<div class="page-heading">
<div>
<h1>订单详情</h1>
<p>查看订单信息、商品明细与履约进度</p>
</div>
<el-button class="back-btn" :icon="ArrowLeft" @click="goBack">返回列表</el-button>
</div>
<!-- 顶部一体化状态与履约进度卡片 -->
<el-card shadow="never" class="top-overview-card">
<!-- 第一层:返回导航 + 核心状态/单号 + 操作按钮组 -->
<div class="overview-header">
<div class="header-left">
<el-button link class="back-btn" @click="goBack">
<el-icon><ArrowLeft /></el-icon>
<span>返回列表</span>
</el-button>
<div class="header-divider"></div>
<el-tag
:type="getOrderStatusTagType(orderInfo.order.orderStatus)"
effect="dark"
effect="light"
class="main-status-tag"
>
{{ orderInfo.orderStatusValue || "加载中..." }}
@@ -26,6 +28,7 @@
type="primary"
:icon="CopyDocument"
class="copy-icon-btn"
aria-label="复制订单编号"
@click="copyText(orderInfo.order.sn || sn, '订单编号')"
/>
</el-tooltip>
@@ -207,6 +210,7 @@
link
type="primary"
:icon="CopyDocument"
aria-label="复制手机号"
@click="copyText(orderInfo.order.consigneeMobile, '手机号')"
/>
</el-tooltip>
@@ -349,7 +353,6 @@
<el-table
v-loading="loading"
border
:data="data"
class="goods-table"
style="width: 100%"
@@ -517,7 +520,7 @@
<el-dialog v-model="orderCancelModal" title="取消订单" width="480px" destroy-on-close>
<el-form ref="orderCancelFormRef" :model="orderCancelForm" label-width="90px" :rules="orderCancelValidate">
<el-form-item label="取消原因" prop="reason">
<el-input v-model="orderCancelForm.reason" type="textarea" :rows="3" placeholder="请输入取消订单的具体原因" />
<el-input v-model="orderCancelForm.reason" type="textarea" :rows="3" resize="none" placeholder="请输入取消订单的具体原因" />
</el-form-item>
</el-form>
<template #footer>
@@ -1403,510 +1406,229 @@ onMounted(() => {
<style lang="scss" scoped>
.order-detail-page {
--detail-ink: #1e293b;
--detail-muted: #748196;
--detail-line: #e8edf4;
width: 100%;
padding-bottom: 28px;
max-width: 1680px;
margin: 0 auto;
padding: 8px 12px 32px;
box-sizing: border-box;
color: var(--detail-ink);
// 顶部一体化状态与履约步骤卡片
.top-overview-card {
border-radius: 10px;
border: 1px solid #e2e8f0;
background: #fff;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03);
:deep(.el-card__body) {
padding: 16px 22px 18px;
}
.overview-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 14px;
padding-bottom: 16px;
border-bottom: 1px solid #f1f5f9;
.header-left {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
.back-btn {
font-size: 14px;
font-weight: 600;
color: #475569;
padding: 4px 2px;
&:hover {
color: var(--el-color-primary);
}
.el-icon {
margin-right: 4px;
font-size: 15px;
}
}
.header-divider {
width: 1px;
height: 16px;
background: #cbd5e1;
}
.main-status-tag {
font-size: 13px;
font-weight: 600;
padding: 0 12px;
height: 28px;
border-radius: 6px;
}
.sn-title-group {
display: flex;
align-items: center;
font-size: 14px;
.sn-label {
color: #64748b;
}
.sn-value {
font-size: 15px;
font-weight: 700;
color: #0f172a;
letter-spacing: 0.3px;
}
.copy-icon-btn {
margin-left: 4px;
font-size: 15px;
}
}
.meta-tags {
display: flex;
align-items: center;
gap: 8px;
}
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
}
// 紧凑型横向履约进度条
.fulfillment-stepper {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16px;
padding: 14px 24px;
background: #f8fafc;
border: 1px solid #f1f5f9;
border-radius: 8px;
gap: 12px;
.step-item {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
&:last-child {
flex: 0 0 auto;
}
.step-node {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.step-circle {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 700;
border: 2px solid #cbd5e1;
background: #fff;
color: #94a3b8;
transition: all 0.2s ease;
flex-shrink: 0;
}
.step-text {
display: flex;
flex-direction: column;
gap: 2px;
.step-title {
font-size: 14px;
font-weight: 600;
color: #64748b;
line-height: 1.3;
}
.step-desc {
font-size: 12px;
color: #94a3b8;
line-height: 1.3;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
}
}
.step-connector {
flex: 1;
height: 2px;
background: #e2e8f0;
margin: 0 20px;
border-radius: 2px;
min-width: 30px;
}
// 已完成状态
&.is-done {
.step-circle {
border-color: #10b981;
background: #ecfdf5;
color: #10b981;
}
.step-text .step-title {
color: #0f172a;
}
.step-text .step-desc {
color: #10b981;
}
.step-connector {
background: #10b981;
}
}
// 当前进行中状态
&.is-active {
.step-circle {
border-color: #3b82f6;
background: #3b82f6;
color: #fff;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.14);
}
.step-text .step-title {
color: #1e293b;
font-weight: 700;
}
.step-text .step-desc {
color: #3b82f6;
font-weight: 500;
}
}
// 已取消状态
&.is-cancelled {
.step-circle {
border-color: #e2e8f0;
background: #f1f5f9;
color: #cbd5e1;
}
}
}
}
}
// 中部三列等高卡片网格
.info-grid-row {
margin-bottom: 16px;
.page-heading {
display: flex;
flex-wrap: wrap;
:deep(.el-col) {
display: flex;
margin-bottom: 0;
}
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 22px;
h1 { margin: 0; font-size: 24px; font-weight: 650; line-height: 34px; letter-spacing: .5px; }
p { margin: 5px 0 0; color: var(--detail-muted); font-size: 13px; line-height: 22px; }
.back-btn { border-radius: 8px; color: #526078; }
}
.section-card {
border-radius: 10px;
border: 1px solid #e2e8f0;
.top-overview-card, .section-card {
border: 1px solid var(--detail-line);
border-radius: 12px;
background: #fff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03);
width: 100%;
box-shadow: 0 3px 12px rgba(31, 51, 82, .025);
}
&.equal-card {
display: flex;
flex-direction: column;
height: 100%;
.top-overview-card {
margin-bottom: 20px;
background: linear-gradient(115deg, #f5f9ff 0, #fff 58%);
:deep(.el-card__body) { padding: 24px 26px; }
}
.overview-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
}
.header-left {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
min-width: 0;
.main-status-tag { height: 34px; padding: 0 14px; border-radius: 7px; font-size: 15px; font-weight: 600; }
.sn-title-group { display: flex; align-items: center; min-width: 0; font-size: 13px; }
.sn-label { color: var(--detail-muted); white-space: nowrap; }
.sn-value { color: #22324b; font-size: 16px; font-weight: 650; overflow-wrap: anywhere; }
.copy-icon-btn { margin-left: 4px; }
.meta-tags { display: flex; gap: 6px; }
.meta-tags .el-tag { background: #fff; border-color: #e1e8f0; color: #748196; border-radius: 4px; }
}
.header-actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
.el-button { margin: 0; border-radius: 7px; font-size: 13px; }
}
:deep(.el-card__body) {
flex: 1;
}
}
:deep(.el-card__header) {
padding: 13px 18px;
border-bottom: 1px solid #f1f5f9;
background: #fafbfc;
}
:deep(.el-card__body) {
padding: 16px 18px;
}
.card-title {
.fulfillment-stepper {
display: flex;
align-items: center;
margin-top: 24px;
padding: 24px 8px 2px;
border-top: 1px solid #e5edf8;
gap: 12px;
.step-item { display: flex; align-items: center; flex: 1; min-width: 0; }
.step-item:last-child { flex: 0 0 auto; }
.step-node { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
.step-circle {
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
width: 34px;
height: 34px;
box-sizing: border-box;
flex-shrink: 0;
border-radius: 50%;
border: 1px solid #dce3ed;
background: #f7f9fc;
color: #8b98ab;
font-size: 14px;
font-weight: 600;
color: #1e293b;
.el-icon {
font-size: 16px;
color: #ff6a00;
}
&.justify-between {
justify-content: space-between;
width: 100%;
}
.flex-title {
display: flex;
align-items: center;
gap: 8px;
}
.count-badge {
margin-left: 4px;
font-weight: 500;
}
}
.step-title { color: #8290a4; font-size: 14px; font-weight: 600; line-height: 22px; }
.step-desc { margin-top: 4px; color: #8b98ab; font-size: 12px; line-height: 18px; font-variant-numeric: tabular-nums; }
.step-connector { flex: 1; height: 2px; min-width: 16px; margin: 0 24px; background: #e8edf4; }
.is-done {
.step-circle { color: #20a684; background: #eaf8f2; border-color: #c7eadd; }
.step-title { color: #45566b; }
.step-connector { background: #b9e3d5; }
}
.is-active {
.step-circle { background: #3875ed; border-color: #3875ed; color: #fff; box-shadow: 0 0 0 5px #eaf1ff; }
.step-title, .step-desc { color: #326bdb; }
}
.is-cancelled .step-circle { color: #a3adbb; background: #f1f4f8; }
}
// 水平对齐键值对列表
.info-grid-row {
display: flex;
flex-wrap: wrap;
row-gap: 16px;
margin-bottom: 20px;
:deep(.el-col) { display: flex; }
}
.section-card {
width: 100%;
&.equal-card { display: flex; flex-direction: column; height: 100%; }
&.equal-card :deep(.el-card__body) { flex: 1; }
:deep(.el-card__header) { padding: 18px 22px; background: #fff; border-bottom: 1px solid var(--detail-line); }
:deep(.el-card__body) { padding: 20px 22px; }
.card-title, .flex-title { display: flex; align-items: center; gap: 10px; }
.card-title { min-height: 24px; color: #25354b; font-size: 15px; font-weight: 600; }
.card-title > .el-icon, .flex-title > .el-icon {
box-sizing: content-box;
padding: 6px;
font-size: 16px;
color: #4c7bdd;
background: #eff4ff;
border-radius: 7px;
}
.justify-between { justify-content: space-between; }
.count-badge { margin-left: 4px; font-size: 12px; font-weight: 400; color: #748196; border: 0; background: #f3f5f9; }
}
.kv-list {
display: flex;
flex-direction: column;
gap: 12px;
.kv-row {
display: flex;
align-items: flex-start;
font-size: 13px;
line-height: 20px;
.kv-label {
width: 82px;
flex-shrink: 0;
color: #64748b;
font-weight: 400;
}
.kv-val {
flex: 1;
min-width: 0;
color: #1e293b;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
word-break: break-all;
&.address-val {
line-height: 20px;
color: #334155;
}
&.flex-col {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.member-name-text {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: bottom;
}
.sub-pay-method {
font-size: 12px;
color: #64748b;
}
}
.form-ans-line {
font-size: 13px;
color: #334155;
line-height: 20px;
.f-label {
color: #64748b;
}
}
}
gap: 17px;
.kv-row { display: flex; align-items: flex-start; gap: 12px; font-size: 13px; line-height: 22px; }
.kv-label { width: 76px; flex-shrink: 0; color: var(--detail-muted); }
.kv-val { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.address-val { line-height: 23px; }
.flex-col { flex-direction: column; align-items: flex-start; gap: 4px; }
.member-name-text { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sub-pay-method, .f-label { font-size: 12px; color: var(--detail-muted); }
.form-ans-line { font-size: 13px; line-height: 22px; }
}
// 商品表格与底部结算卡
.goods-section-card {
margin-bottom: 0;
.goods-table {
:deep(th.el-table__cell) {
background-color: #f8fafc;
color: #475569;
font-weight: 600;
height: 44px;
}
}
.goods-table {
--el-table-border-color: #edf1f6;
--el-table-header-bg-color: #f7f9fc;
--el-table-row-hover-bg-color: #f8faff;
color: #43516a;
:deep(th.el-table__cell) { height: 46px; background: #f7f9fc; color: #6b788d; font-size: 12px; font-weight: 500; }
:deep(td.el-table__cell) { padding: 18px 0; }
:deep(.cell) { padding-left: 16px; padding-right: 16px; }
}
// 商品列表单元格
.goods-item-cell {
display: flex;
align-items: center;
gap: 12px;
padding: 4px 0;
.goods-thumb {
width: 56px;
height: 56px;
border-radius: 6px;
border: 1px solid #e2e8f0;
flex-shrink: 0;
}
.goods-info {
flex: 1;
min-width: 0;
.goods-name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
line-height: 18px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.goods-specs {
margin-top: 6px;
display: flex;
flex-wrap: wrap;
gap: 6px;
.spec-tag {
font-size: 11px;
color: #64748b;
background: #f1f5f9;
padding: 1px 6px;
border-radius: 4px;
}
}
}
gap: 16px;
.goods-thumb { width: 68px; height: 68px; border-radius: 8px; border: 1px solid #e8edf4; flex-shrink: 0; }
.goods-info { flex: 1; min-width: 0; }
.goods-name { color: #26364d; font-size: 14px; font-weight: 600; line-height: 22px; overflow-wrap: anywhere; }
.goods-specs { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }
.spec-tag { font-size: 12px; color: #7b8799; background: #f4f6f9; padding: 2px 8px; border-radius: 4px; }
}
.sub-header-title {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
font-weight: 600;
color: #1e293b;
margin-bottom: 10px;
.el-icon {
color: #ff6a00;
}
}
// 商品清单底部金额汇总排版
.sub-header-title { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; font-weight: 600; }
.sub-header-title .el-icon { color: #4c7bdd; }
.goods-summary-footer {
display: flex;
justify-content: flex-end;
margin-top: 16px;
.summary-rows {
width: 340px;
background: #f8fafc;
border: 1px solid #f1f5f9;
border-radius: 8px;
padding: 14px 20px;
display: flex;
flex-direction: column;
gap: 8px;
.summary-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
.label {
color: #64748b;
}
.value {
color: #1e293b;
font-weight: 500;
}
&.final-total {
padding-top: 4px;
align-items: baseline;
.label {
font-size: 14px;
font-weight: 600;
color: #0f172a;
}
.final-price {
font-size: 20px;
font-weight: 700;
}
}
}
.summary-divider {
height: 1px;
background: #e2e8f0;
margin: 4px 0;
}
}
margin-top: 24px;
.summary-rows { width: 360px; max-width: 100%; padding: 20px 24px; box-sizing: border-box; background: #f7f9fc; border: 1px solid #edf1f7; border-radius: 10px; display: flex; flex-direction: column; gap: 12px; }
.summary-item { display: flex; align-items: center; justify-content: space-between; gap: 16px; font-size: 13px; }
.label { color: var(--detail-muted); }
.value { font-weight: 500; }
.summary-divider { height: 1px; background: #e1e7f0; margin: 4px 0; }
.final-total { align-items: baseline; }
.final-total .label { color: #334155; font-size: 14px; font-weight: 600; }
.final-price { font-size: 26px; font-weight: 650; white-space: nowrap; letter-spacing: -.5px; }
}
.addr-select-line {
display: flex;
align-items: center;
gap: 10px;
}
.text-danger { color: #ef4444; }
.text-success { color: #10b981; }
.text-warning { color: #f59e0b; }
.text-primary { color: #2563eb; }
.text-gray { color: #94a3b8; }
.text-dark { color: #0f172a; }
.addr-select-line { display: flex; align-items: center; gap: 10px; }
.text-danger { color: #e45454; }
.text-success { color: #20a684; }
.text-warning { color: #d9972b; }
.text-primary { color: #3875ed; }
.text-gray { color: #8b98ab; }
.text-dark { color: #1e293b; }
.font-bold { font-weight: 600; }
.font-mono { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; }
.font-mono { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace; font-variant-numeric: tabular-nums; }
.mt_16 { margin-top: 16px; }
.mb_10 { margin-bottom: 10px; }
.pr_12 { padding-right: 12px; }
@media (max-width: 1440px) {
padding: 6px 4px 28px;
.overview-header { align-items: flex-start; }
.header-actions { width: 100%; padding-top: 16px; border-top: 1px solid #e5edf8; }
.fulfillment-stepper { margin-top: 20px; }
.section-card :deep(.el-card__header), .section-card :deep(.el-card__body) { padding-left: 18px; padding-right: 18px; }
.kv-list .kv-row { gap: 8px; }
.kv-list .kv-label { width: 70px; }
.fulfillment-stepper .step-connector { margin: 0 16px; }
}
@media (max-width: 1100px) {
.info-grid-row :deep(.el-col) { width: 100%; max-width: 100%; flex: 0 0 100%; }
.fulfillment-stepper .step-node { flex-direction: column; align-items: flex-start; gap: 10px; }
.fulfillment-stepper .step-item { align-items: flex-start; }
.fulfillment-stepper .step-connector { margin-top: 17px; }
.fulfillment-stepper .step-desc { max-width: 120px; }
}
@media (max-width: 600px) {
padding: 4px 0 20px;
.page-heading { margin-bottom: 16px; }
.page-heading h1 { font-size: 21px; }
.page-heading p { max-width: 190px; }
.top-overview-card :deep(.el-card__body) { padding: 18px 16px; }
.header-left { gap: 10px; }
.header-left .sn-title-group { width: 100%; }
.header-left .sn-value { font-size: 13px; }
.fulfillment-stepper { align-items: stretch; flex-direction: column; gap: 20px; padding: 20px 4px 0; }
.fulfillment-stepper .step-item { flex: auto; }
.fulfillment-stepper .step-node { flex-direction: row; align-items: center; }
.fulfillment-stepper .step-desc { max-width: none; }
.fulfillment-stepper .step-connector { display: none; }
.goods-summary-footer .summary-rows { width: 100%; padding: 18px; }
.goods-summary-footer .final-price { font-size: 23px; }
.section-card .flex-title { flex-wrap: wrap; }
}
}
</style>