| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- <template>
- <div class="app-page">
- <section class="glass-card section-card header-section">
- <div class="order-detail-header">
- <div class="header-left">
- <div class="header-top">
- <el-button @click="$router.back()">返回列表</el-button>
- <el-button type="primary" @click="handleContact">联系买家</el-button>
- </div>
- <h2>订单详情 <span class="order-no">{{ order.orderNo }}</span></h2>
- <div class="header-tags">
- <el-tag v-if="order.channelOrderNo" type="info">渠道: {{ order.channelOrderNo }}</el-tag>
- <el-tag v-if="order.priority === 'urgent'" type="danger">加急</el-tag>
- <el-tag v-if="order.buyerLevel" :type="buyerLevelType(order.buyerLevel)">{{ order.buyerLevel }}</el-tag>
- <el-tag v-for="tag in (order.orderTags || [])" :key="tag" size="small" type="warning">{{ tag }}</el-tag>
- </div>
- </div>
- <div class="header-actions">
- <el-button @click="handlePrint">打印面单</el-button>
- <el-button @click="handlePrintInvoice">打印发货单</el-button>
- </div>
- </div>
- <div class="stat-grid">
- <article class="stat-card">
- <div class="stat-card__icon" :style="{ background: statusColor }">
- <el-icon><Check /></el-icon>
- </div>
- <div class="stat-card__content">
- <div class="stat-card__label">订单状态</div>
- <div class="stat-card__value">{{ statusLabel(order.orderStatus ?? '') }}</div>
- </div>
- </article>
- <article class="stat-card">
- <div class="stat-card__icon" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)">
- <el-icon><Money /></el-icon>
- </div>
- <div class="stat-card__content">
- <div class="stat-card__label">实付金额</div>
- <div class="stat-card__value">{{ order.currency }} {{ order.actualPaid || order.amount }}</div>
- </div>
- </article>
- <article class="stat-card">
- <div class="stat-card__icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)">
- <el-icon><Van /></el-icon>
- </div>
- <div class="stat-card__content">
- <div class="stat-card__label">发货状态</div>
- <div class="stat-card__value">{{ order.shippingStatus || '待发货' }}</div>
- </div>
- </article>
- <article class="stat-card">
- <div class="stat-card__icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)">
- <el-icon><Warning /></el-icon>
- </div>
- <div class="stat-card__content">
- <div class="stat-card__label">异常标签</div>
- <el-tag :type="order.exceptionTag === '正常' ? 'success' : 'warning'" size="default">{{ order.exceptionTag || '正常' }}</el-tag>
- </div>
- </article>
- <article class="stat-card">
- <div class="stat-card__icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)">
- <el-icon><OfficeBuilding /></el-icon>
- </div>
- <div class="stat-card__content">
- <div class="stat-card__label">分配仓库</div>
- <div class="stat-card__value">{{ order.warehouse || '待分配' }}</div>
- </div>
- </article>
- </div>
- </section>
- <div class="detail-grid">
- <div class="detail-left">
- <article class="glass-card section-card">
- <div class="section-header">
- <h3>买家信息</h3>
- </div>
- <el-descriptions :column="2" border size="default">
- <el-descriptions-item label="买家ID">{{ order.buyerId }}</el-descriptions-item>
- <el-descriptions-item label="买家昵称">{{ order.buyer }}</el-descriptions-item>
- <el-descriptions-item label="邮箱">{{ order.buyerEmail }}</el-descriptions-item>
- <el-descriptions-item label="电话">{{ order.buyerPhone }}</el-descriptions-item>
- <el-descriptions-item label="国家">{{ order.buyerCountry }} {{ getCountryFlag(order.buyerCountry) }}</el-descriptions-item>
- <el-descriptions-item label="会员等级">{{ order.buyerLevel || '-' }}</el-descriptions-item>
- <el-descriptions-item label="注册时间">{{ order.buyerRegisterTime || '-' }}</el-descriptions-item>
- <el-descriptions-item label="历史订单">{{ order.buyerOrderCount || 0 }} 单</el-descriptions-item>
- <el-descriptions-item label="历史消费">{{ order.buyerTotalSpent || '-' }}</el-descriptions-item>
- <el-descriptions-item label="设备信息">{{ order.device }} / {{ order.browser }} / {{ order.os }}</el-descriptions-item>
- </el-descriptions>
- </article>
- <article class="glass-card section-card">
- <div class="section-header">
- <h3>收货信息</h3>
- <el-button link type="primary" @click="editAddressDialog = true" :disabled="order.orderStatus === 'cancelled'">修改地址</el-button>
- </div>
- <el-descriptions :column="2" border size="default">
- <el-descriptions-item label="收货人">
- <span class="value-bold">{{ order.receiverName }}</span>
- <el-button link type="primary" size="small" @click="handleCall">拨打电话</el-button>
- </el-descriptions-item>
- <el-descriptions-item label="联系电话">{{ order.receiverPhone }}</el-descriptions-item>
- <el-descriptions-item label="国家/地区">{{ order.receiverCountry }} {{ getCountryFlag(order.receiverCountry) }}</el-descriptions-item>
- <el-descriptions-item label="州/省">{{ order.receiverState || '-' }}</el-descriptions-item>
- <el-descriptions-item label="城市">{{ order.receiverCity }}</el-descriptions-item>
- <el-descriptions-item label="区县">{{ order.receiverDistrict || '-' }}</el-descriptions-item>
- <el-descriptions-item label="邮编">{{ order.receiverPostalCode || '-' }}</el-descriptions-item>
- <el-descriptions-item label="详细地址" :span="2">
- <span class="address-text">{{ order.receiverAddress }}</span>
- </el-descriptions-item>
- <el-descriptions-item label="坐标">{{ order.latitude?.toFixed(4) }}, {{ order.longitude?.toFixed(4) }}</el-descriptions-item>
- <el-descriptions-item label="买家备注">
- <span class="buyer-remark">{{ order.buyerRemark || '无' }}</span>
- </el-descriptions-item>
- </el-descriptions>
- </article>
- <article class="glass-card section-card">
- <div class="section-header">
- <h3>支付信息</h3>
- </div>
- <el-descriptions :column="2" border size="default">
- <el-descriptions-item label="支付方式">{{ order.paymentMethod }}</el-descriptions-item>
- <el-descriptions-item label="支付时间">{{ order.paymentTime || '-' }}</el-descriptions-item>
- <el-descriptions-item label="交易流水">{{ order.transactionId || '-' }}</el-descriptions-item>
- <el-descriptions-item label="订单货币">{{ order.currency }} (汇率: {{ order.exchangeRate || 1 }})</el-descriptions-item>
- <el-descriptions-item label="商品金额">{{ order.orderAmount || order.amount }}</el-descriptions-item>
- <el-descriptions-item label="折合CNY">¥{{ order.orderAmountCNY || '-' }}</el-descriptions-item>
- <el-descriptions-item label="税额">{{ order.taxAmount || '-' }}</el-descriptions-item>
- <el-descriptions-item label="运费">{{ order.shippingFee || '0.00' }}</el-descriptions-item>
- <el-descriptions-item label="优惠折扣" :span="2">
- <span v-if="order.couponCode" class="discount-tag">
- {{ order.couponCode }} (-{{ order.couponDiscount || order.discountAmount || '0.00' }})
- </span>
- <span v-else>-</span>
- </el-descriptions-item>
- <el-descriptions-item label="实付金额">
- <span class="value-bold actual-paid">{{ order.currency }} {{ order.actualPaid || order.amount }}</span>
- </el-descriptions-item>
- <el-descriptions-item label="退款金额">{{ order.refundAmount || '0.00' }}</el-descriptions-item>
- </el-descriptions>
- </article>
- <article class="glass-card section-card">
- <div class="section-header">
- <h3>物流追踪</h3>
- <el-button link type="primary" @click="trackMore">查看更多</el-button>
- </div>
- <div v-if="order.trackingNo" class="tracking-info">
- <div class="tracking-info__main">
- <el-tag type="success" size="default">{{ order.carrier || '顺丰速运' }}</el-tag>
- <span class="tracking-no">{{ order.trackingNo }}</span>
- <el-button link type="primary" @click="copyTrackingNo">复制</el-button>
- <el-button link type="primary" @click="openTrackingUrl">查询物流</el-button>
- </div>
- <div class="tracking-meta">
- <span>配送方式: {{ order.shippingMethod || '-' }}</span>
- <span>仓库: {{ order.warehouse || '-' }}</span>
- <span>库位: {{ order.warehouseLocation || '-' }}</span>
- </div>
- <el-timeline class="track-timeline" v-if="trackingSteps.length">
- <el-timeline-item v-for="(step, index) in trackingSteps" :key="index" :timestamp="step.time" :type="step.type" :hollow="index === 0">
- <p class="track-title">{{ step.title }}</p>
- <p class="track-detail" v-if="step.detail">{{ step.detail }}</p>
- </el-timeline-item>
- </el-timeline>
- <el-empty v-else description="暂无物流信息" />
- </div>
- <div v-else class="tracking-empty">
- <el-empty description="暂无物流信息" />
- <el-button v-if="order.orderStatus === 'shipped'" type="primary" size="default" @click="addTrackingDialog = true">添加物流信息</el-button>
- </div>
- </article>
- <article class="glass-card section-card">
- <div class="section-header">
- <h3>营销归因</h3>
- </div>
- <el-descriptions :column="2" border size="default">
- <el-descriptions-item label="来源(UTM)">{{ order.utmSource || '-' }}</el-descriptions-item>
- <el-descriptions-item label="媒介(UTM)">{{ order.utmMedium || '-' }}</el-descriptions-item>
- <el-descriptions-item label="活动(UTM)">{{ order.utmCampaign || '-' }}</el-descriptions-item>
- <el-descriptions-item label="内容(UTM)">{{ order.utmContent || '-' }}</el-descriptions-item>
- <el-descriptions-item label="关键词(UTM)">{{ order.utmTerm || '-' }}</el-descriptions-item>
- <el-descriptions-item label="下单IP">{{ order.ip || '-' }}</el-descriptions-item>
- <el-descriptions-item label="IP归属国">{{ order.ipCountry || '-' }}</el-descriptions-item>
- <el-descriptions-item label="落地页">{{ order.landingPage || '-' }}</el-descriptions-item>
- </el-descriptions>
- </article>
- </div>
- <div class="detail-right">
- <article class="glass-card section-card">
- <h3>快捷操作</h3>
- <div class="action-menu">
- <el-dropdown trigger="click" @command="handleActionCommand">
- <el-button type="primary">
- 订单操作 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
- </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="assign" :disabled="order.orderStatus === 'cancelled'">
- <el-icon><Van /></el-icon> 提交仓库
- </el-dropdown-item>
- <el-dropdown-item command="lock" :disabled="order.orderStatus === 'cancelled'">
- <el-icon><Lock /></el-icon> 锁定库存
- </el-dropdown-item>
- <el-dropdown-item command="split" :disabled="order.orderStatus === 'cancelled'">
- <el-icon><Connection /></el-icon> 拆单
- </el-dropdown-item>
- <el-dropdown-item command="merge" :disabled="order.orderStatus === 'cancelled'">
- <el-icon><FolderMerged /></el-icon> 合单
- </el-dropdown-item>
- <el-dropdown-item command="address" :disabled="order.orderStatus === 'cancelled'">
- <el-icon><Edit /></el-icon> 修改地址
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-dropdown trigger="click" @command="handlePrintCommand">
- <el-button>
- 打印 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
- </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="label">
- <el-icon><Printer /></el-icon> 打印面单
- </el-dropdown-item>
- <el-dropdown-item command="invoice">
- <el-icon><Document /></el-icon> 打印发货单
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-dropdown trigger="click" @command="handleRefundCommand">
- <el-button type="success">
- 退款 <el-icon class="el-icon--right"><ArrowDown /></el-icon>
- </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="refund" :disabled="!canRefund">
- <el-icon><Money /></el-icon> 发起退款
- </el-dropdown-item>
- <el-dropdown-item command="viewRefund">
- <el-icon><Tickets /></el-icon> 查看退款记录
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-button type="danger" plain @click="cancelDialog = true" :disabled="order.orderStatus === 'cancelled'">
- 取消订单
- </el-button>
- </div>
- </article>
- <article class="glass-card section-card">
- <h3>订单关联</h3>
- <el-descriptions :column="1" border size="default">
- <el-descriptions-item label="母订单">{{ order.parentOrderId || '-' }}</el-descriptions-item>
- <el-descriptions-item label="子订单">{{ order.childOrderIds?.join(', ') || '-' }}</el-descriptions-item>
- <el-descriptions-item label="合单订单">{{ order.mergeOrderId || '-' }}</el-descriptions-item>
- <el-descriptions-item label="关联订单">{{ order.relatedOrderId || '-' }}</el-descriptions-item>
- <el-descriptions-item label="原订单(退款/重发)">{{ order.originalOrderId || '-' }}</el-descriptions-item>
- </el-descriptions>
- </article>
- <article class="glass-card section-card">
- <h3>客服信息</h3>
- <el-descriptions :column="1" border size="default">
- <el-descriptions-item label="处理人">{{ order.handler || '待分配' }}</el-descriptions-item>
- <el-descriptions-item label="处理组">{{ order.handlerGroup || '-' }}</el-descriptions-item>
- </el-descriptions>
- </article>
- <article class="glass-card section-card">
- <h3>内部备注</h3>
- <el-input v-model="order.internalRemark" type="textarea" :rows="3" placeholder="客服内部备注信息" />
- <el-button type="primary" size="default" @click="saveRemark" class="save-btn">保存备注</el-button>
- </article>
- <article class="glass-card section-card">
- <h3>操作日志</h3>
- <el-timeline>
- <el-timeline-item v-for="(log, index) in operationLogs" :key="index" :timestamp="log.time" :type="log.type" :hollow="index === 0">
- <p class="log-title">{{ log.title }}</p>
- <p class="log-detail" v-if="log.operator">操作人: {{ log.operator }} ({{ log.operatorRole }})</p>
- </el-timeline-item>
- </el-timeline>
- </article>
- </div>
- </div>
- <section class="glass-card section-card products-section">
- <div class="section-header">
- <h3>商品明细 ({{ order.items?.length || 0 }} 件)</h3>
- </div>
- <el-table :data="order.items" stripe>
- <el-table-column prop="sku" label="SKU编码" width="160">
- <template #default="{ row }">
- <span class="sku-code">{{ row.sku }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="productTitle" label="商品信息" min-width="260">
- <template #default="{ row }">
- <div class="product-info">
- <el-avatar v-if="row.productImage" :src="row.productImage" :size="56" shape="square" />
- <div v-else class="product-image-placeholder">暂无图</div>
- <div class="product-detail">
- <div class="product-title">{{ row.productTitle }}</div>
- <div class="product-category">{{ row.categoryName }} | {{ row.skuId }}</div>
- </div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="specs" label="规格" width="160">
- <template #default="{ row }">
- <div class="specs-list">
- <el-tag v-for="spec in row.specs" :key="spec.specName" size="small">{{ spec.specValue }}</el-tag>
- <span v-if="!row.specs || row.specs.length === 0">-</span>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="barcode" label="条码" width="140">
- <template #default="{ row }">
- <span class="barcode">{{ row.barcode || '-' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="qty" label="数量" width="90" align="center">
- <template #default="{ row }">
- <span class="qty">{{ row.qty }}</span>
- <div class="qty-detail" v-if="row.shippedQty || row.returnedQty">
- <span>已发: {{ row.shippedQty || 0 }}</span>
- <span>退: {{ row.returnedQty || 0 }}</span>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="price" label="单价" width="100" align="right">
- <template #default="{ row }">
- <span>{{ order.currency }} {{ row.price }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="costPrice" label="成本价" width="100" align="right">
- <template #default="{ row }">
- <span class="cost-price">{{ order.currency }} {{ row.costPrice }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="profit" label="利润" width="100" align="right">
- <template #default="{ row }">
- <span class="profit">{{ order.currency }} {{ row.profit }}</span>
- <div class="profit-rate">{{ row.profitRate }}%</div>
- </template>
- </el-table-column>
- <el-table-column prop="subtotal" label="小计" width="100" align="right">
- <template #default="{ row }">
- <span class="subtotal">{{ order.currency }} {{ row.subtotal }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="inventory" label="库存" width="150" align="center">
- <template #default="{ row }">
- <div class="inventory-info">
- <span class="inv-available">可用: {{ row.available }}</span>
- <span class="inv-locked">锁定: {{ row.locked }}</span>
- <span class="inv-transit">在途: {{ row.inTransit }}</span>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="giftFlag" label="赠品" width="80" align="center">
- <template #default="{ row }">
- <el-tag v-if="row.giftFlag" type="warning" size="small">赠品</el-tag>
- <span v-else>-</span>
- </template>
- </el-table-column>
- </el-table>
- <div class="amount-summary">
- <div class="summary-row">
- <span>商品总额:</span>
- <span>{{ order.currency }} {{ order.orderAmount || order.amount }}</span>
- </div>
- <div class="summary-row">
- <span>税额:</span>
- <span>{{ order.currency }} {{ order.taxAmount || '0.00' }}</span>
- </div>
- <div class="summary-row">
- <span>运费:</span>
- <span>{{ order.currency }} {{ order.shippingFee || '0.00' }}</span>
- </div>
- <div class="summary-row" v-if="order.discountAmount && parseFloat(order.discountAmount) > 0">
- <span>优惠:</span>
- <span class="discount">-{{ order.currency }} {{ order.discountAmount }}</span>
- </div>
- <div class="summary-row" v-if="order.couponDiscount && parseFloat(order.couponDiscount) > 0">
- <span>优惠券:</span>
- <span class="discount">-{{ order.currency }} {{ order.couponDiscount }} ({{ order.couponCode }})</span>
- </div>
- <div class="summary-row summary-row--total">
- <span>实付金额:</span>
- <span class="total-amount">{{ order.currency }} {{ order.actualPaid || order.amount }}</span>
- </div>
- <div class="summary-row summary-row--profit">
- <span>订单利润:</span>
- <span class="profit-amount">{{ order.currency }} {{ totalProfit }}</span>
- <span class="profit-rate">({{ totalProfitRate }}%)</span>
- </div>
- </div>
- </section>
- <section class="glass-card section-card package-section">
- <div class="section-header">
- <h3>包裹信息</h3>
- </div>
- <el-descriptions :column="4" border size="default">
- <el-descriptions-item label="重量">{{ order.weight ? order.weight.toFixed(2) + ' kg' : '-' }}</el-descriptions-item>
- <el-descriptions-item label="体积">L: {{ order.length || '-' }}cm x W: {{ order.width || '-' }}cm x H: {{ order.height || '-' }}cm</el-descriptions-item>
- <el-descriptions-item label="仓库">{{ order.warehouse || '-' }}</el-descriptions-item>
- <el-descriptions-item label="库位">{{ order.warehouseLocation || '-' }}</el-descriptions-item>
- </el-descriptions>
- </section>
- <el-dialog v-model="cancelDialog" title="取消订单" width="520px">
- <el-alert title="取消订单需二次确认,操作不可撤销" type="warning" :closable="false" style="margin-bottom:16px" />
- <el-form label-position="top">
- <el-form-item label="取消原因" required>
- <el-select v-model="cancelReason" style="width:100%">
- <el-option label="买家申请取消" value="buyer_cancel" />
- <el-option label="地址异常无法发货" value="address_error" />
- <el-option label="库存不足" value="out_of_stock" />
- <el-option label="商品缺货" value="goods_unavailable" />
- <el-option label="其他原因" value="other" />
- </el-select>
- </el-form-item>
- <el-form-item label="补充说明">
- <el-input v-model="cancelRemark" type="textarea" :rows="3" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="cancelDialog = false">关闭</el-button>
- <el-button type="danger" @click="confirmCancel">确认取消</el-button>
- </template>
- </el-dialog>
- <el-dialog v-model="splitDialog" title="拆单处理" width="620px">
- <el-alert title="拆单后原订单将变更为主订单,新订单将从原订单拆分" type="info" :closable="false" style="margin-bottom:16px" />
- <el-table :data="order.items">
- <el-table-column prop="sku" label="SKU" min-width="160" />
- <el-table-column prop="productTitle" label="商品" min-width="180" show-overflow-tooltip />
- <el-table-column prop="qty" label="原数量" width="90" align="center" />
- <el-table-column label="主单数量" width="120">
- <template #default="{ row }">
- <el-input-number v-model="row.mainQty" :min="0" :max="row.qty" size="small" @change="updateSplitQty(row)" />
- </template>
- </el-table-column>
- <el-table-column label="新单数量" width="100">
- <template #default="{ row }">
- <span class="split-qty">{{ row.qty - (row.mainQty || 0) }}</span>
- </template>
- </el-table-column>
- </el-table>
- <template #footer>
- <el-button @click="splitDialog = false">取消</el-button>
- <el-button type="primary" @click="confirmSplit">确认拆单</el-button>
- </template>
- </el-dialog>
- <el-dialog v-model="mergeDialog" title="合单处理" width="480px">
- <el-form label-position="top">
- <el-form-item label="目标订单号" required>
- <el-input v-model="mergeTarget" placeholder="请输入待合并的订单号" />
- </el-form-item>
- <el-form-item>
- <el-alert title="合单要求:两个订单买家信息一致,且均未发货" type="warning" :closable="false" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="mergeDialog = false">取消</el-button>
- <el-button type="primary" @click="confirmMerge">确认合单</el-button>
- </template>
- </el-dialog>
- <el-dialog v-model="editAddressDialog" title="修改收货地址" width="520px">
- <el-form :model="editAddress" label-position="top">
- <el-form-item label="收货人" required>
- <el-input v-model="editAddress.receiverName" placeholder="请输入收货人姓名" />
- </el-form-item>
- <el-form-item label="联系电话" required>
- <el-input v-model="editAddress.receiverPhone" placeholder="请输入联系电话" />
- </el-form-item>
- <el-form-item label="国家" required>
- <el-input v-model="editAddress.receiverCountry" placeholder="请输入国家" />
- </el-form-item>
- <el-form-item label="州/省">
- <el-input v-model="editAddress.receiverState" placeholder="请输入州/省" />
- </el-form-item>
- <el-form-item label="城市" required>
- <el-input v-model="editAddress.receiverCity" placeholder="请输入城市" />
- </el-form-item>
- <el-form-item label="邮编">
- <el-input v-model="editAddress.receiverPostalCode" placeholder="请输入邮编" />
- </el-form-item>
- <el-form-item label="详细地址" required>
- <el-input v-model="editAddress.receiverAddress" type="textarea" :rows="3" placeholder="请输入详细地址" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="editAddressDialog = false">取消</el-button>
- <el-button type="primary" @click="confirmEditAddress">确认修改</el-button>
- </template>
- </el-dialog>
- <el-drawer v-model="assignDrawer" title="分配仓库" size="400px">
- <el-form label-position="top">
- <el-form-item label="选择仓库" required>
- <el-select v-model="order.warehouse" style="width:100%">
- <el-option label="深圳南山仓" value="深圳南山仓" />
- <el-option label="义乌商贸仓" value="义乌商贸仓" />
- <el-option label="洛杉矶海外仓" value="洛杉矶海外仓" />
- </el-select>
- </el-form-item>
- <el-form-item label="库位">
- <el-input v-model="order.warehouseLocation" placeholder="可选填写库位号" />
- </el-form-item>
- <el-form-item label="仓库备注">
- <el-input v-model="warehouseRemark" type="textarea" :rows="4" placeholder="可选填写仓库操作备注" />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="assignDrawer = false">取消</el-button>
- <el-button type="primary" @click="submitWarehouse">确认提交</el-button>
- </template>
- </el-drawer>
- </div>
- </template>
- <script setup lang="ts">
- import { onMounted, reactive, ref, computed } from 'vue';
- import { useRoute } from 'vue-router';
- import { ElMessage, ElMessageBox } from 'element-plus';
- import { api } from '@/api/services';
- import type { OrderItem, OrderProductItem } from '@/types/page';
- const route = useRoute();
- const cancelDialog = ref(false);
- const splitDialog = ref(false);
- const mergeDialog = ref(false);
- const assignDrawer = ref(false);
- const editAddressDialog = ref(false);
- const cancelReason = ref('');
- const cancelRemark = ref('');
- const mergeTarget = ref('');
- const warehouseRemark = ref('');
- const canRefund = computed(() => ['paid', 'allocated', 'shipped'].includes(order.orderStatus || ''));
- const countryFlags: Record<string, string> = {
- 'US': '🇺🇸', 'UK': '🇬🇧', 'GB': '🇬🇧', 'JP': '🇯🇵', 'DE': '🇩🇪', 'FR': '🇫🇷',
- 'CA': '🇨🇦', 'AU': '🇦🇺', 'IT': '🇮🇹', 'ES': '🇪🇸', 'NL': '🇳🇱', 'BR': '🇧🇷',
- 'MX': '🇲🇽', 'KR': '🇰🇷', 'CN': '🇨🇳', 'HK': '🇭🇰', 'TW': '🇹🇼', 'SG': '🇸🇬'
- };
- const getCountryFlag = (country: string): string => countryFlags[country] || '🌍';
- const statusColor = computed(() => {
- const colors: Record<string, string> = {
- created: 'linear-gradient(135deg, #909399 0%, #b1b3b8 100%)',
- paid: 'linear-gradient(135deg, #409eff 0%, #66b1ff 100%)',
- allocated: 'linear-gradient(135deg, #e6a23c 0%, #ebb563 100%)',
- shipped: 'linear-gradient(135deg, #67c23a 0%, #85ce61 100%)',
- delivered: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
- completed: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
- cancelled: 'linear-gradient(135deg, #f56c6c 0%, #f78989 100%)',
- refunded: 'linear-gradient(135deg, #f56c6c 0%, #f78989 100%)'
- };
- return colors[order.orderStatus || ''] || colors.created;
- });
- const order = reactive<OrderItem>({
- id: '', orderNo: '', channelOrderNo: '', channel: '', orderStatus: '', shippingStatus: '', paymentStatus: '', exceptionTag: '', priority: 'normal',
- createdAt: '', updatedAt: '', paidAt: '', shippedAt: '', deliveredAt: '',
- buyerId: '', buyer: '', buyerEmail: '', buyerPhone: '', buyerCountry: '', buyerLevel: '', buyerTags: [], buyerRegisterTime: '', buyerOrderCount: 0, buyerTotalSpent: '',
- receiverName: '', receiverPhone: '', receiverCountry: '', receiverState: '', receiverCity: '', receiverDistrict: '', receiverPostalCode: '', receiverAddress: '', receiverAddressLang: '', latitude: 0, longitude: 0,
- transactionId: '', paymentMethod: '', paymentTime: '', currency: 'USD', exchangeRate: 1, orderAmount: '', orderAmountCNY: '', taxAmount: '', shippingFee: '', discountAmount: '', couponCode: '', couponDiscount: '', actualPaid: '', refundAmount: '', refundStatus: '',
- shippingMethod: '', carrier: '', trackingNo: '', trackingUrl: '', warehouse: '', warehouseLocation: '', estimatedDelivery: '', weight: 0, length: 0, width: 0, height: 0,
- utmSource: '', utmMedium: '', utmCampaign: '', utmContent: '', utmTerm: '', referrerUrl: '', landingPage: '', ip: '', ipCountry: '', device: '', browser: '', os: '',
- parentOrderId: '', childOrderIds: [], mergeOrderId: '', relatedOrderId: '', originalOrderId: '',
- handler: '', handlerGroup: '', orderTags: [], internalRemark: '', buyerRemark: '',
- itemCount: 0, amount: '', items: [], timeline: [], logs: []
- });
- const operationLogs = ref<{ time: string; title: string; type: string; operator?: string; operatorRole?: string }[]>([]);
- const trackingSteps = ref<{ time: string; title: string; detail?: string; type: string }[]>([]);
- const editAddress = reactive({ receiverName: '', receiverPhone: '', receiverCountry: '', receiverState: '', receiverCity: '', receiverPostalCode: '', receiverAddress: '' });
- const statusLabel = (s: string) => {
- const map: Record<string, string> = { created: '待支付', paid: '已支付', allocated: '已分配', shipped: '已发货', delivered: '已签收', completed: '已完成', cancelled: '已取消', refunded: '已退款' };
- return map[s] || s;
- };
- const buyerLevelType = (level: string) => {
- const map: Record<string, string> = { 'VIP': 'danger', '黄金': 'warning', '普通': 'info' };
- return map[level] || 'info';
- };
- const refundStatusType = (status: string) => {
- const map: Record<string, string> = { '无退款': 'info', '部分退款': 'warning', '已全额退款': 'danger' };
- return map[status] || 'info';
- };
- const totalProfit = computed(() => {
- if (!order.items || order.items.length === 0) return '0.00';
- return order.items.reduce((sum, item) => sum + (parseFloat(item.profit) || 0), 0).toFixed(2);
- });
- const totalProfitRate = computed(() => {
- if (!order.items || order.items.length === 0) return '0';
- const totalAmount = order.items.reduce((sum, item) => sum + (parseFloat(item.subtotal) || 0), 0);
- const totalCost = order.items.reduce((sum, item) => sum + (parseFloat(item.costPrice) * item.qty || 0), 0);
- if (totalCost === 0) return '0';
- return ((parseFloat(totalProfit.value) / totalCost) * 100).toFixed(1);
- });
- const loadData = async () => {
- const id = route.query.id as string;
- if (!id) return;
- Object.assign(order, generateMockOrderDetail(id));
- };
- const generateMockOrderDetail = (id: string): Partial<OrderItem> => {
- const orderNo = `OMS-20260419-0012`;
- const items: OrderProductItem[] = [
- {
- id: 'item-1',
- productId: 'prod-001',
- skuId: 'skuid-001',
- sku: 'SKU-LUGG-20-BLK',
- productTitle: 'TravelFlex Expandable Carry-On 20寸 行李箱 / Black 黑色',
- productImage: '',
- categoryId: 'cat-001',
- categoryName: '行李箱',
- specs: [
- { specName: '尺寸', specValue: '20寸' },
- { specName: '颜色', specValue: '黑色' }
- ],
- barcode: `BC${Date.now()}001`,
- qty: 1,
- price: '89.00',
- costPrice: '45.00',
- profit: '44.00',
- profitRate: 49,
- subtotal: '89.00',
- weight: 3.5,
- available: 45,
- locked: 5,
- inTransit: 20,
- reservedQty: 0,
- shippedQty: 0,
- returnedQty: 0,
- giftFlag: false
- },
- {
- id: 'item-2',
- productId: 'prod-002',
- skuId: 'skuid-002',
- sku: 'SKU-TAG-SET-GRY',
- productTitle: 'Travel Tag Set 旅行标签牌套装 / Gray 灰色',
- productImage: '',
- categoryId: 'cat-002',
- categoryName: '旅行配件',
- specs: [
- { specName: '颜色', specValue: '灰色' }
- ],
- barcode: `BC${Date.now()}002`,
- qty: 2,
- price: '19.50',
- costPrice: '8.00',
- profit: '23.00',
- profitRate: 59,
- subtotal: '39.00',
- weight: 0.2,
- available: 120,
- locked: 10,
- inTransit: 50,
- reservedQty: 0,
- shippedQty: 0,
- returnedQty: 0,
- giftFlag: false
- }
- ];
- const amount = items.reduce((sum, item) => sum + parseFloat(item.subtotal), 0);
- return {
- id,
- orderNo,
- channelOrderNo: `CH${Date.now()}`,
- channel: 'Shopify US',
- orderStatus: 'paid',
- shippingStatus: 'unshipped',
- paymentStatus: 'paid',
- exceptionTag: '正常',
- priority: 'normal',
- createdAt: '2026-04-19 20:42:00',
- updatedAt: '2026-04-19 21:16:00',
- paidAt: '2026-04-19 20:43:00',
- shippedAt: '',
- deliveredAt: '',
- buyerId: 'buyer-1001',
- buyer: 'Olivia Zhang',
- buyerEmail: 'olivia.zhang@example.com',
- buyerPhone: '+1 213-555-4401',
- buyerCountry: 'US',
- buyerLevel: 'VIP',
- buyerTags: ['高价值'],
- buyerRegisterTime: '2025-06-15',
- buyerOrderCount: 12,
- buyerTotalSpent: '$1,234.56',
- receiverName: 'Olivia Zhang',
- receiverPhone: '+1 213-555-4401',
- receiverCountry: 'US',
- receiverState: 'California',
- receiverCity: 'Los Angeles',
- receiverDistrict: 'Downtown',
- receiverPostalCode: '90001',
- receiverAddress: '1234 Main St, Apt 5B, Los Angeles, CA 90001, United States',
- receiverAddressLang: '',
- latitude: 34.0522,
- longitude: -118.2437,
- transactionId: `TXN${Date.now()}`,
- paymentMethod: 'Visa (****4242)',
- paymentTime: '2026-04-19 20:43:00',
- currency: 'USD',
- exchangeRate: 1,
- orderAmount: amount.toFixed(2),
- orderAmountCNY: (amount * 7.2).toFixed(2),
- taxAmount: (amount * 0.08).toFixed(2),
- shippingFee: '0.00',
- discountAmount: '5.00',
- couponCode: 'SAVE10',
- couponDiscount: '5.00',
- actualPaid: (amount - 5).toFixed(2),
- refundAmount: '0.00',
- refundStatus: '无退款',
- shippingMethod: '标快',
- carrier: '顺丰速运',
- trackingNo: '',
- trackingUrl: '',
- warehouse: '深圳南山仓',
- warehouseLocation: 'A-12-C',
- estimatedDelivery: '2026-04-25',
- weight: 3.7,
- length: 55,
- width: 35,
- height: 25,
- utmSource: 'google',
- utmMedium: 'cpc',
- utmCampaign: 'Spring Sale 2026',
- utmContent: 'Product luggage',
- utmTerm: 'carry on luggage',
- referrerUrl: 'https://www.google.com/search?q=carry+on+luggage',
- landingPage: '/product/luggage-001',
- ip: '192.168.1.100',
- ipCountry: 'US',
- device: 'iPhone',
- browser: 'Safari',
- os: 'iOS',
- parentOrderId: '',
- childOrderIds: [],
- mergeOrderId: '',
- relatedOrderId: '',
- originalOrderId: '',
- handler: '陈欣',
- handlerGroup: '运营组A',
- orderTags: ['VIP客户'],
- internalRemark: '地址楼栋不清晰,需要客服复核后再提交仓库',
- buyerRemark: 'Please deliver to door',
- itemCount: items.reduce((sum, item) => sum + item.qty, 0),
- amount: amount.toFixed(2),
- items,
- timeline: [],
- logs: []
- };
- };
- const updateSplitQty = (row: OrderProductItem) => {
- row.mainQty = row.mainQty || 0;
- };
- const lockInventory = () => { ElMessage.success('库存已锁定'); };
- const confirmCancel = async () => {
- if (!cancelReason.value) { ElMessage.warning('请选择取消原因'); return; }
- await ElMessageBox.confirm('确认取消此订单?操作不可撤销。', '二次确认', { type: 'warning' });
- await api.updateOrder(order.id!, { orderStatus: 'cancelled' } as Partial<OrderItem>);
- order.orderStatus = 'cancelled';
- operationLogs.value.unshift({ time: '刚刚', title: '订单已取消', summary: cancelReason.value, type: 'danger', operator: '客服', operatorRole: '客服组' });
- cancelDialog.value = false;
- ElMessage.success('订单已取消');
- };
- const confirmSplit = () => {
- const hasSplit = order.items?.some(i => (i.mainQty || 0) < i.qty);
- if (!hasSplit) { ElMessage.warning('请至少设置一个 SKU 的主单数量小于原数量'); return; }
- splitDialog.value = false;
- operationLogs.value.unshift({ time: '刚刚', title: '拆单方案已生成', summary: '待仓库确认后生成新发货单', type: 'primary', operator: '客服', operatorRole: '客服组' });
- ElMessage.success('拆单方案已生成');
- };
- const confirmMerge = () => {
- if (!mergeTarget.value) { ElMessage.warning('请输入目标订单号'); return; }
- mergeDialog.value = false;
- ElMessage.success(`已提交与 ${mergeTarget.value} 的合单申请`);
- };
- const submitWarehouse = async () => {
- await api.updateOrder(order.id!, {
- warehouse: order.warehouse,
- warehouseLocation: order.warehouseLocation,
- orderStatus: 'allocated'
- } as Partial<OrderItem>);
-
- for (const item of order.items || []) {
- if (item.skuId) {
- const inventoryRes = await api.getInventory();
- const invItem = inventoryRes.items.find((inv: any) => inv.skuId === item.skuId);
- if (invItem) {
- const newAvailable = Math.max(0, (invItem.available || 0) - item.qty);
- const newLocked = (invItem.locked || 0) + item.qty;
- await api.updateInventory(invItem.id, {
- locked: newLocked,
- available: newAvailable
- });
- await api.createInventoryLog({
- source: '订单分配锁定',
- relatedOrder: order.orderNo,
- operator: '客服',
- quantity: -item.qty,
- afterQty: newAvailable,
- time: new Date().toISOString()
- });
- }
- }
- }
-
- order.orderStatus = 'allocated';
- assignDrawer.value = false;
- operationLogs.value.unshift({ time: '刚刚', title: '已提交仓库', summary: `分配至 ${order.warehouse} (${order.warehouseLocation}),库存已锁定`, type: 'success', operator: '客服', operatorRole: '客服组' });
- ElMessage.success(`已提交仓库,订单状态已更新为已分配,${order.items?.length || 0} 个 SKU 库存已锁定`);
- };
- const confirmEditAddress = () => {
- if (!editAddress.receiverName || !editAddress.receiverPhone || !editAddress.receiverAddress) {
- ElMessage.warning('请填写完整地址信息');
- return;
- }
- Object.assign(order, {
- receiverName: editAddress.receiverName,
- receiverPhone: editAddress.receiverPhone,
- receiverCountry: editAddress.receiverCountry,
- receiverState: editAddress.receiverState,
- receiverCity: editAddress.receiverCity,
- receiverPostalCode: editAddress.receiverPostalCode,
- receiverAddress: editAddress.receiverAddress
- });
- editAddressDialog.value = false;
- operationLogs.value.unshift({ time: '刚刚', title: '收货地址已修改', summary: `${editAddress.receiverName}, ${editAddress.receiverCity}`, type: 'warning', operator: '客服', operatorRole: '客服组' });
- ElMessage.success('地址已修改');
- };
- const saveRemark = () => {
- ElMessage.success('备注已保存');
- };
- const handlePrint = () => { ElMessage.success(`正在生成 ${order.orderNo} 的面单...`); };
- const handlePrintInvoice = () => { ElMessage.success(`正在生成 ${order.orderNo} 的发货单...`); };
- const handleContact = () => { ElMessage.info(`正在打开与 ${order.buyer} 的对话窗口...`); };
- const handleRefund = () => { ElMessage.success('正在发起退款流程...'); };
- const handleCall = () => { ElMessage.info(`正在拨打 ${order.receiverPhone}...`); };
- const copyTrackingNo = () => {
- if (order.trackingNo) {
- navigator.clipboard.writeText(order.trackingNo);
- ElMessage.success('复制成功');
- }
- };
- const openTrackingUrl = () => { ElMessage.info('正在打开物流追踪页面...'); };
- const trackMore = () => { ElMessage.info('正在加载完整物流信息...'); };
- const handleActionCommand = (command: string) => {
- switch (command) {
- case 'assign': assignDrawer.value = true; break;
- case 'lock': lockInventory(); break;
- case 'split': splitDialog.value = true; break;
- case 'merge': mergeDialog.value = true; break;
- case 'address': editAddressDialog.value = true; break;
- }
- };
- const handlePrintCommand = (command: string) => {
- if (command === 'label') {
- handlePrint();
- } else if (command === 'invoice') {
- handlePrintInvoice();
- }
- };
- const handleRefundCommand = (command: string) => {
- if (command === 'refund') {
- handleRefund();
- } else if (command === 'viewRefund') {
- ElMessage.info('正在打开退款记录...');
- }
- };
- onMounted(() => {
- loadData();
- operationLogs.value = [
- { time: '2026-04-19 21:16', title: '地址校验告警', type: 'warning', operator: '系统', operatorRole: '自动' },
- { time: '2026-04-19 21:10', title: '库存锁定完成', type: 'success', operator: '系统', operatorRole: '自动' },
- { time: '2026-04-19 20:43', title: '支付确认', type: 'primary', operator: '支付网关', operatorRole: '自动' },
- { time: '2026-04-19 20:42', title: '订单接入 OMS', type: 'primary', operator: '渠道同步', operatorRole: '自动' }
- ];
- });
- </script>
- <style scoped>
- .order-detail-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- padding-bottom: 20px;
- border-bottom: 1px solid #f0f0f0;
- }
- .header-left { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
- .header-left h2 { margin: 0; font-size: 22px; display: flex; align-items: center; gap: 12px; }
- .order-no { color: var(--cb-primary); font-size: 18px; }
- .header-actions { display: flex; gap: 10px; }
- .stat-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
- .stat-card { display: flex; align-items: center; gap: 16px; padding: 18px 16px; background: #fafafa; border-radius: 12px; transition: all 0.2s; }
- .stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
- .stat-card__icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: #fff; flex-shrink: 0; }
- .stat-card__content { flex: 1; min-width: 0; }
- .stat-card__label { font-size: 13px; color: #888; margin-bottom: 4px; }
- .stat-card__value { font-size: 17px; font-weight: 600; color: #333; }
- .detail-grid { display: grid; grid-template-columns: 1fr 360px; gap: 20px; margin-top: 20px; }
- .detail-left, .detail-right { display: flex; flex-direction: column; gap: 20px; }
- .glass-card { padding: 20px; }
- .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #f5f5f5; }
- .section-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #333; }
- .value-bold { font-weight: 600; }
- .address-text { line-height: 1.7; color: #555; }
- .tracking-info__main { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 12px 16px; background: #f8f9fa; border-radius: 8px; }
- .tracking-no { font-family: 'Monaco', 'Menlo', monospace; font-size: 15px; font-weight: 600; letter-spacing: 0.5px; }
- .tracking-meta { display: flex; gap: 20px; font-size: 14px; color: #666; margin-bottom: 20px; }
- .track-timeline { padding: 0 8px; }
- .track-title { margin: 0; font-weight: 600; }
- .track-detail { margin: 6px 0 0; color: #999; font-size: 13px; }
- .tracking-empty { text-align: center; padding: 32px 0; }
- .action-menu { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
- .action-menu .el-dropdown { display: flex; }
- .action-menu .el-button { display: flex; align-items: center; gap: 6px; }
- :deep(.el-dropdown-menu__item) {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 16px;
- font-size: 14px;
- }
- :deep(.el-dropdown-menu__item .el-icon) {
- font-size: 16px;
- }
- .log-title { margin: 0; font-weight: 600; }
- .log-detail { margin: 6px 0 0; color: #999; font-size: 13px; }
- .product-info { display: flex; gap: 14px; align-items: flex-start; }
- .product-image-placeholder { width: 56px; height: 56px; background: #f5f5f5; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #999; flex-shrink: 0; }
- .product-detail { display: flex; flex-direction: column; gap: 5px; }
- .product-title { font-weight: 600; font-size: 14px; line-height: 1.4; }
- .product-category { font-size: 12px; color: #999; }
- .sku-code, .barcode { font-family: 'Monaco', 'Menlo', monospace; font-size: 12px; }
- .specs-list { display: flex; flex-wrap: wrap; gap: 5px; }
- .qty { font-weight: 700; font-size: 17px; }
- .qty-detail { display: flex; gap: 10px; font-size: 11px; color: #999; margin-top: 4px; }
- .cost-price { color: #999; }
- .profit { color: #67c23a; font-weight: 600; }
- .profit-rate { font-size: 11px; color: #67c23a; }
- .subtotal { font-weight: 700; color: var(--cb-accent); }
- .inventory-info { display: flex; flex-direction: column; gap: 3px; font-size: 12px; }
- .inv-available { color: #67c23a; }
- .inv-locked { color: #e6a23c; }
- .inv-transit { color: #409eff; }
- .amount-summary { margin-top: 20px; padding: 20px; background: #fafafa; border-radius: 10px; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; }
- .summary-row { display: flex; gap: 48px; font-size: 14px; width: 100%; justify-content: flex-end; }
- .summary-row span:first-child { color: #666; min-width: 90px; text-align: right; }
- .summary-row span:last-child { min-width: 110px; text-align: right; }
- .summary-row--total { font-size: 17px; font-weight: 600; padding-top: 12px; border-top: 1px solid #eee; }
- .total-amount { color: var(--cb-accent); font-size: 20px; }
- .summary-row--profit { color: #67c23a; }
- .profit-amount { font-weight: 600; }
- .profit-rate { color: #67c23a; }
- .discount { color: #67c23a; }
- .glass-card.section-card { padding: 24px; }
- .header-section { padding: 24px; }
- .header-top { display: flex; gap: 10px; margin-bottom: 12px; }
- .header-tags { display: flex; gap: 8px; flex-wrap: wrap; }
- .buyer-remark { color: #888; font-style: italic; }
- .discount-tag { color: #67c23a; font-weight: 500; }
- .actual-paid { color: var(--cb-accent); font-size: 16px; }
- .products-section { margin-top: 20px; }
- .package-section { margin-top: 20px; }
- .save-btn { margin-top: 12px; }
- </style>
|