```
feat(split): 优化分账订单页面UI和提示信息 - 简化错误提示文案,提升用户体验 - 重构分账订单列表页面样式,优化表格布局和响应式显示 - 添加分账来源标签和诊断详情弹窗功能 - 改进长文本内容的显示方式,支持水平滚动查看 - 调整列宽和文字说明,使界面更加清晰易读 ```
这个提交包含在:
@@ -82,7 +82,7 @@ class SplitCalculator
|
||||
$payFee = floatval($order['pay_fee']);
|
||||
$commissionRewards = CommissionReward::where('order_id', $orderId)->where('status', '>=', 0)->select();
|
||||
if ($this->isEmptyResult($commissionRewards)) {
|
||||
$issues[] = '该订单无分销佣金记录(未产生推广关系或未参与分销)';
|
||||
$issues[] = '无分销佣金记录';
|
||||
}
|
||||
|
||||
foreach ($this->toResultList($commissionRewards) as $reward) {
|
||||
@@ -98,7 +98,7 @@ class SplitCalculator
|
||||
$receiver = $this->getReceiver('promoter', $userId);
|
||||
if (!$receiver) {
|
||||
$issues[] = sprintf(
|
||||
'推广员#%d 佣金¥%s 无汇付接收方(分账接收方 role_id 须等于该 user_id)',
|
||||
'推广员#%d 佣金¥%s:未配置汇付接收方(role_id 须等于该 user_id)',
|
||||
$userId,
|
||||
number_format($amount, 2, '.', '')
|
||||
);
|
||||
@@ -131,7 +131,7 @@ class SplitCalculator
|
||||
} elseif (!empty($ruleItems) && ($amountSource === 'rule' || ($amountSource === 'mixed' && empty($commissionItems)))) {
|
||||
$finalSource = 'rule';
|
||||
if ($amountSource === 'mixed' && !empty($issues)) {
|
||||
$issues[] = '已回退到分账规则(如推广员 5%),金额与佣金不一致';
|
||||
$issues[] = '已回退分账规则(如 5%),与佣金金额不一致';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
background: #f5f7fa;
|
||||
min-height: 100%;
|
||||
}
|
||||
#splitOrderIndex .el-table .cell {
|
||||
overflow: hidden;
|
||||
line-height: 1.5;
|
||||
vertical-align: top;
|
||||
}
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
@@ -26,176 +31,142 @@
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.cell-plain {
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cell-sub-plain {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
margin-top: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* 默认截断;悬浮后横向滚动查看完整内容 */
|
||||
.scroll-cell {
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
cursor: default;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
.scroll-cell:hover {
|
||||
overflow-x: auto;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.scroll-cell::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
.scroll-cell::-webkit-scrollbar { height: 3px; }
|
||||
.scroll-cell::-webkit-scrollbar-thumb {
|
||||
background: rgba(116, 56, 213, 0.35);
|
||||
background: rgba(116, 56, 213, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.scroll-cell-inner {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
min-width: min-content;
|
||||
}
|
||||
.scroll-hint {
|
||||
display: none;
|
||||
font-size: 10px;
|
||||
color: #c0c4cc;
|
||||
margin-top: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.scroll-cell:hover + .scroll-hint,
|
||||
.scroll-cell-wrap:hover .scroll-hint {
|
||||
display: block;
|
||||
}
|
||||
.scroll-cell-wrap {
|
||||
max-width: 100%;
|
||||
}
|
||||
.amount-cell {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.amount-cell .money-line {
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.amount-cell .money-line.split-line {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.mono-text {
|
||||
font-family: Consolas, Monaco, "Courier New", monospace;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
.link-text {
|
||||
color: #7438D5;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
.link-text { color: #7438D5; cursor: pointer; }
|
||||
.link-text:hover { opacity: 0.85; }
|
||||
.money-text { color: #e1212b; font-weight: 600; }
|
||||
.channel-tag {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
padding: 0 6px;
|
||||
margin-left: 4px;
|
||||
padding: 0 5px;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
border-radius: 4px;
|
||||
border-radius: 3px;
|
||||
background: #f4f4f5;
|
||||
color: #606266;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.status-tag.s0 { background: #f4f4f5; color: #909399; }
|
||||
.status-tag.s1 { background: #ecf5ff; color: #409eff; }
|
||||
.status-tag.s2 { background: #f0f9eb; color: #67c23a; }
|
||||
.status-tag.s3 { background: #fdf6ec; color: #e6a23c; }
|
||||
.status-tag.s4 { background: #fef0f0; color: #f56c6c; }
|
||||
.recv-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.recv-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
.amount-block {
|
||||
text-align: right;
|
||||
line-height: 1.65;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
}
|
||||
.recv-scroll {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: calc(100% - 120px);
|
||||
}
|
||||
.recv-role {
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
background: #f0ebfa;
|
||||
color: #7438D5;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.recv-name { color: #303133; }
|
||||
.recv-account {
|
||||
color: #909399;
|
||||
font-family: Consolas, Monaco, "Courier New", monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
.recv-amt { color: #e1212b; font-weight: 600; margin-left: auto; white-space: nowrap; }
|
||||
.recv-preview-hint {
|
||||
font-size: 11px;
|
||||
color: #c0c4cc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.fail-reason {
|
||||
font-size: 12px;
|
||||
color: #f56c6c;
|
||||
line-height: 1.5;
|
||||
.amount-block .sub { font-size: 12px; color: #909399; }
|
||||
.source-line {
|
||||
margin-top: 6px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.source-tag {
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.source-tag.commission { background: #ecf5ff; color: #409eff; }
|
||||
.source-tag.rule { background: #fdf6ec; color: #e6a23c; }
|
||||
.diagnose-hint {
|
||||
.source-detail-btn {
|
||||
font-size: 11px;
|
||||
color: #e6a23c;
|
||||
line-height: 1.5;
|
||||
margin-top: 4px;
|
||||
white-space: normal;
|
||||
}
|
||||
.op-hint {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#splitOrderIndex .el-table .cell {
|
||||
.source-detail-btn:hover { color: #7438D5; }
|
||||
.recv-block {
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
.recv-block:last-child { border-bottom: none; padding-bottom: 0; }
|
||||
.recv-line1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.recv-role {
|
||||
flex-shrink: 0;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
background: #f0ebfa;
|
||||
color: #7438D5;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.recv-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #303133;
|
||||
}
|
||||
.recv-amt {
|
||||
flex-shrink: 0;
|
||||
color: #e1212b;
|
||||
font-weight: 600;
|
||||
}
|
||||
.recv-line2 {
|
||||
margin-top: 3px;
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
}
|
||||
.fail-text {
|
||||
font-size: 12px;
|
||||
color: #f56c6c;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.diag-pop {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
max-height: 220px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.diag-pop li { margin-bottom: 6px; }
|
||||
.diag-pop li:last-child { margin-bottom: 0; }
|
||||
.op-hint { font-size: 12px; color: #909399; margin-top: 10px; }
|
||||
</style>
|
||||
<div id="splitOrderIndex" v-cloak>
|
||||
<div class="page-card">
|
||||
<p class="page-intro">
|
||||
<strong>分账记录</strong>展示向支付通道(微信/汇付)发起的<strong>网关分账</strong>结果,与「分销佣金」「区域差价」钱包入账是不同链路。
|
||||
仅<strong>已支付且触发分账</strong>的订单会出现记录;列表为空时请先确认分账已开启并完成一笔测试支付。
|
||||
<strong>分账记录</strong>为支付通道网关分账结果,与分销佣金钱包入账不同链路。
|
||||
长订单号可在单元格内<strong>左右滑动</strong>查看;「规则兜底」时点 <strong>详情</strong> 可看未走佣金分账的原因。
|
||||
</p>
|
||||
</div>
|
||||
<div class="page-card">
|
||||
@@ -214,92 +185,89 @@
|
||||
<el-option label="微信" value="wechat"></el-option>
|
||||
</el-select>
|
||||
<el-date-picker v-model="dateRange" type="daterange" size="small" range-separator="至"
|
||||
start-placeholder="记录开始" end-placeholder="记录结束" value-format="yyyy-MM-dd"></el-date-picker>
|
||||
start-placeholder="开始" end-placeholder="结束" value-format="yyyy-MM-dd"></el-date-picker>
|
||||
<el-button type="primary" size="small" @click="search">搜索</el-button>
|
||||
<el-button size="small" @click="resetFilters">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-card" style="padding-bottom: 8px;">
|
||||
<el-table :data="listData" border stripe v-loading="loading"
|
||||
empty-text="暂无分账记录。请确认已开启分账,并完成支付/确认收货后再查看。">
|
||||
<el-table-column label="记录时间" prop="createtime_text" width="156"></el-table-column>
|
||||
<el-table-column label="买家" min-width="130">
|
||||
empty-text="暂无分账记录">
|
||||
<el-table-column label="时间" prop="createtime_text" width="152"></el-table-column>
|
||||
<el-table-column label="买家" width="128" show-overflow-tooltip>
|
||||
<template slot-scope="s">
|
||||
<div class="scroll-cell-wrap">
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel">
|
||||
<div class="scroll-cell-inner cell-plain">{{ s.row.buyer_display || '—' }}</div>
|
||||
<span>{{ s.row.buyer_display || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单号" min-width="168">
|
||||
<template slot-scope="s">
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel">
|
||||
<div class="scroll-cell-inner">
|
||||
<span class="link-text mono-text" @click="openOrder(s.row.order_id)">{{ s.row.order_sn }}</span>
|
||||
<span class="channel-tag">{{ s.row.channel_text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel" v-if="s.row.out_order_no" style="margin-top:4px;">
|
||||
<div class="scroll-cell-inner mono-text" style="color:#909399;font-size:11px;">{{ s.row.out_order_no }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单号" min-width="200">
|
||||
<el-table-column label="金额 / 来源" width="132" align="right">
|
||||
<template slot-scope="s">
|
||||
<div class="scroll-cell-wrap">
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel">
|
||||
<div class="scroll-cell-inner cell-plain">
|
||||
<span class="link-text mono-text" @click="openOrder(s.row.order_id)">{{ s.row.order_sn || '—' }}</span>
|
||||
<span class="channel-tag">{{ s.row.channel_text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-hint">悬浮可左右滑动</div>
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel" v-if="s.row.out_order_no" style="margin-top:4px;">
|
||||
<div class="scroll-cell-inner cell-sub-plain mono-text">分账 {{ s.row.out_order_no }}</div>
|
||||
</div>
|
||||
<div class="amount-block">
|
||||
<div>订单 <span class="money-text">¥{{ formatMoney(s.row.total_amount) }}</span></div>
|
||||
<div class="sub">分账 <span class="money-text">¥{{ formatMoney(s.row.split_amount) }}</span></div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="138" align="right">
|
||||
<template slot-scope="s">
|
||||
<div class="amount-cell">
|
||||
<div class="money-line">订单 <span class="money-text">¥{{ formatMoney(s.row.total_amount) }}</span></div>
|
||||
<div class="money-line split-line">分账 <span class="money-text">¥{{ formatMoney(s.row.split_amount) }}</span></div>
|
||||
<span v-if="s.row.split_source_text" :class="'source-tag ' + (s.row.split_source === 'commission' ? 'commission' : 'rule')">
|
||||
{{ s.row.split_source_text }}
|
||||
<div class="source-line" v-if="s.row.split_source">
|
||||
<span :class="'source-tag ' + (s.row.split_source === 'commission' ? 'commission' : 'rule')">
|
||||
{{ s.row.split_source === 'commission' ? '按佣金' : '规则兜底' }}
|
||||
</span>
|
||||
<el-popover
|
||||
v-if="s.row.split_source === 'rule' && s.row.split_diagnose && s.row.split_diagnose.length"
|
||||
placement="left"
|
||||
width="300"
|
||||
trigger="click">
|
||||
<ul class="diag-pop">
|
||||
<li v-for="(t, i) in s.row.split_diagnose" :key="i">{{ t }}</li>
|
||||
</ul>
|
||||
<span slot="reference" class="source-detail-btn">详情</span>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分账接收方" min-width="260">
|
||||
<el-table-column label="分账给谁" min-width="168">
|
||||
<template slot-scope="s">
|
||||
<div v-if="s.row.receiver_details && s.row.receiver_details.length" class="recv-list">
|
||||
<div v-for="(r, idx) in s.row.receiver_details" :key="idx" class="recv-row">
|
||||
<span class="recv-role">{{ r.role_label }}</span>
|
||||
<div class="scroll-cell recv-scroll" @wheel="scrollCellWheel" v-if="r.display_name || r.account">
|
||||
<div class="scroll-cell-inner">
|
||||
<span class="recv-name">{{ r.display_name }}</span>
|
||||
<span class="recv-account" v-if="r.account"> {{ r.account }}</span>
|
||||
</div>
|
||||
<template v-if="s.row.receiver_details && s.row.receiver_details.length">
|
||||
<div v-for="(r, idx) in s.row.receiver_details" :key="idx" class="recv-block">
|
||||
<div class="recv-line1">
|
||||
<span class="recv-role">{{ r.role_label }}</span>
|
||||
<span class="recv-name" :title="r.display_name">{{ r.display_name }}</span>
|
||||
<span class="recv-amt">¥{{ formatMoney(r.amount) }}</span>
|
||||
</div>
|
||||
<div class="recv-line2 scroll-cell" @wheel="scrollCellWheel" v-if="r.account">
|
||||
<span class="scroll-cell-inner mono-text">{{ r.account }}</span>
|
||||
</div>
|
||||
<span class="recv-amt">¥{{ formatMoney(r.amount) }}</span>
|
||||
</div>
|
||||
<div v-if="s.row.receiver_preview" class="recv-preview-hint">按当前规则预估(历史失败单未落库明细)</div>
|
||||
<div v-if="s.row.split_diagnose && s.row.split_diagnose.length && s.row.split_source === 'rule'" class="diagnose-hint">
|
||||
{{ s.row.split_diagnose.join(';') }}
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="cell-sub-plain">—</span>
|
||||
<div v-if="s.row.receiver_preview" style="font-size:11px;color:#c0c4cc;margin-top:4px;">预估明细</div>
|
||||
</template>
|
||||
<span v-else style="color:#909399;">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="88" align="center">
|
||||
<el-table-column label="状态" width="76" align="center">
|
||||
<template slot-scope="s">
|
||||
<span :class="'status-tag s' + s.row.status">{{ s.row.status_text }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="失败原因" min-width="140">
|
||||
<el-table-column label="失败原因" min-width="120">
|
||||
<template slot-scope="s">
|
||||
<div v-if="s.row.fail_reason" class="scroll-cell-wrap">
|
||||
<div class="scroll-cell" @wheel="scrollCellWheel">
|
||||
<div class="scroll-cell-inner fail-reason">{{ s.row.fail_reason }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="cell-sub-plain">—</span>
|
||||
<div v-if="s.row.fail_reason" class="fail-text" :title="s.row.fail_reason">{{ s.row.fail_reason }}</div>
|
||||
<span v-else style="color:#909399;">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重试" width="56" align="center" prop="retry_count"></el-table-column>
|
||||
<el-table-column label="操作" width="156" fixed="right">
|
||||
<el-table-column label="操作" width="128" fixed="right">
|
||||
<template slot-scope="s">
|
||||
<el-button type="text" v-if="s.row.status == 4" @click="retry(s.row)">重试分账</el-button>
|
||||
<el-button type="text" @click="query(s.row.id)">同步状态</el-button>
|
||||
<el-button type="text" size="small" v-if="s.row.status == 4" @click="retry(s.row)">重试</el-button>
|
||||
<el-button type="text" size="small" @click="query(s.row.id)">同步</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -315,9 +283,7 @@
|
||||
@size-change="onSizeChange">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<p class="op-hint" style="margin-top:10px;">
|
||||
订单号、分账单号等长字段<strong>鼠标悬浮后可左右滑动</strong>查看完整内容;「重试分账」仅对失败记录有效;「同步状态」只拉取通道结果,不会重复扣款。
|
||||
</p>
|
||||
<p class="op-hint">「重试」仅对失败记录向通道重新发起;「同步」只更新状态,不会重复扣款。</p>
|
||||
</div>
|
||||
</div>
|
||||
<script src="__CDN__/assets/addons/shopro/libs/vue.js"></script>
|
||||
|
||||
在新工单中引用
屏蔽一个用户