```
feat(split-order): 添加分账订单失败原因详情展示功能 - 新增 fail_reason_full 字段用于存储完整的失败原因信息 - 实现 formatFailReasonFull 方法解析微信分账失败的详细信息 - 优化前端表格展示,增加查看完整失败原因的弹窗功能 - 调整表格列宽度和样式,提升买家信息展示效果 - 修复昵称和角色ID显示逻辑,避免重复显示问题 ```
这个提交包含在:
@@ -147,6 +147,7 @@ class Order extends Base
|
|||||||
$data['receiver_preview'] = $receiverPreview;
|
$data['receiver_preview'] = $receiverPreview;
|
||||||
$data['receiver_summary'] = $summaryLines ? implode(';', $summaryLines) : '—';
|
$data['receiver_summary'] = $summaryLines ? implode(';', $summaryLines) : '—';
|
||||||
$data['fail_reason'] = $this->formatFailReason($data['wx_result'] ?? '', $status);
|
$data['fail_reason'] = $this->formatFailReason($data['wx_result'] ?? '', $status);
|
||||||
|
$data['fail_reason_full'] = $this->formatFailReasonFull($data['wx_result'] ?? '', $status);
|
||||||
|
|
||||||
$diag = (new SplitCalculator())->diagnose($orderId);
|
$diag = (new SplitCalculator())->diagnose($orderId);
|
||||||
$data['split_source'] = $diag['final_source'];
|
$data['split_source'] = $diag['final_source'];
|
||||||
@@ -264,8 +265,8 @@ class Order extends Base
|
|||||||
$mobile = is_array($user) ? trim((string) ($user['mobile'] ?? '')) : '';
|
$mobile = is_array($user) ? trim((string) ($user['mobile'] ?? '')) : '';
|
||||||
if ($nickname !== '') {
|
if ($nickname !== '') {
|
||||||
$label = $nickname;
|
$label = $nickname;
|
||||||
if ($roleId > 0) {
|
if ($roleId > 0 && (string) $nickname !== (string) $roleId) {
|
||||||
$label .= '#' . $roleId;
|
$label .= ' #' . $roleId;
|
||||||
}
|
}
|
||||||
if ($mobile !== '') {
|
if ($mobile !== '') {
|
||||||
$label .= ' ' . $this->maskMobile($mobile);
|
$label .= ' ' . $this->maskMobile($mobile);
|
||||||
@@ -326,7 +327,38 @@ class Order extends Base
|
|||||||
return implode(' ', $parts);
|
return implode(' ', $parts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mb_strlen($raw) > 120 ? (mb_substr($raw, 0, 120) . '…') : $raw;
|
$full = $this->formatFailReasonFull($wxResult, $status);
|
||||||
|
if ($full === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return mb_strlen($full) > 48 ? (mb_substr($full, 0, 48) . '…') : $full;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatFailReasonFull($wxResult, $status)
|
||||||
|
{
|
||||||
|
if ($status !== 4 || $wxResult === '' || $wxResult === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$raw = is_string($wxResult) ? $wxResult : json_encode($wxResult, JSON_UNESCAPED_UNICODE);
|
||||||
|
if ($raw === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (strpos($raw, 'fund_freeze_stat=FREEZE') !== false) {
|
||||||
|
return '微信资金冻结,需发货/确认收货后再分账';
|
||||||
|
}
|
||||||
|
$decoded = json_decode($raw, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$parts = array_filter([
|
||||||
|
$decoded['resp_desc'] ?? '',
|
||||||
|
isset($decoded['resp_code']) ? ('resp_code=' . $decoded['resp_code']) : '',
|
||||||
|
$decoded['bank_message'] ?? '',
|
||||||
|
$decoded['trans_stat'] ?? '',
|
||||||
|
]);
|
||||||
|
if ($parts) {
|
||||||
|
return implode(',', $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatBuyerDisplay($nickname, $mobile, $userId)
|
protected function formatBuyerDisplay($nickname, $mobile, $userId)
|
||||||
|
|||||||
@@ -7,14 +7,13 @@
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
#splitOrderIndex .el-table .cell {
|
#splitOrderIndex .el-table .cell {
|
||||||
overflow: hidden;
|
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
vertical-align: middle;
|
vertical-align: top;
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
}
|
}
|
||||||
#splitOrderIndex .el-table .col-money .cell,
|
#splitOrderIndex .el-table .col-money .cell,
|
||||||
#splitOrderIndex .el-table .col-source .cell {
|
#splitOrderIndex .el-table .col-source .cell,
|
||||||
overflow: visible;
|
#splitOrderIndex .el-table .col-buyer .cell {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.page-card {
|
.page-card {
|
||||||
@@ -122,12 +121,12 @@
|
|||||||
border-bottom: 1px dashed #ebeef5;
|
border-bottom: 1px dashed #ebeef5;
|
||||||
}
|
}
|
||||||
.recv-block:last-child { border-bottom: none; padding-bottom: 0; }
|
.recv-block:last-child { border-bottom: none; padding-bottom: 0; }
|
||||||
.recv-line1 {
|
.recv-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
min-width: 0;
|
|
||||||
}
|
}
|
||||||
.recv-role {
|
.recv-role {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -138,33 +137,45 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
.recv-name {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
.recv-amt {
|
.recv-amt {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
color: #e1212b;
|
color: #e1212b;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.recv-name-line {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.recv-line2 {
|
.recv-line2 {
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
}
|
}
|
||||||
.fail-text {
|
.fail-cell { font-size: 12px; line-height: 1.55; }
|
||||||
font-size: 12px;
|
.fail-brief {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
line-height: 1.5;
|
word-break: break-word;
|
||||||
word-break: break-all;
|
white-space: normal;
|
||||||
display: -webkit-box;
|
}
|
||||||
-webkit-line-clamp: 2;
|
.fail-more {
|
||||||
-webkit-box-orient: vertical;
|
display: inline-block;
|
||||||
overflow: hidden;
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #7438D5;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.fail-pop-full {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: #606266;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
max-height: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
.diag-pop {
|
.diag-pop {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -209,12 +220,12 @@
|
|||||||
<el-table :data="listData" border stripe v-loading="loading"
|
<el-table :data="listData" border stripe v-loading="loading"
|
||||||
empty-text="暂无分账记录">
|
empty-text="暂无分账记录">
|
||||||
<el-table-column label="时间" prop="createtime_text" width="152"></el-table-column>
|
<el-table-column label="时间" prop="createtime_text" width="152"></el-table-column>
|
||||||
<el-table-column label="买家" width="128" show-overflow-tooltip>
|
<el-table-column label="买家" class-name="col-buyer" min-width="148" show-overflow-tooltip>
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<span>{{ s.row.buyer_display || '—' }}</span>
|
<span>{{ s.row.buyer_display || '—' }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="订单号" min-width="168">
|
<el-table-column label="订单号" min-width="176">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div class="scroll-cell" @wheel="scrollCellWheel">
|
<div class="scroll-cell" @wheel="scrollCellWheel">
|
||||||
<div class="scroll-cell-inner">
|
<div class="scroll-cell-inner">
|
||||||
@@ -237,7 +248,7 @@
|
|||||||
<span class="money-num highlight">¥{{ formatMoney(s.row.split_amount) }}</span>
|
<span class="money-num highlight">¥{{ formatMoney(s.row.split_amount) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="分账来源" class-name="col-source" width="112" align="center">
|
<el-table-column label="分账来源" class-name="col-source" width="118" align="center">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div class="source-cell" v-if="s.row.split_source">
|
<div class="source-cell" v-if="s.row.split_source">
|
||||||
<span :class="'source-tag ' + (s.row.split_source === 'commission' ? 'commission' : 'rule')">
|
<span :class="'source-tag ' + (s.row.split_source === 'commission' ? 'commission' : 'rule')">
|
||||||
@@ -257,15 +268,17 @@
|
|||||||
<span v-else style="color:#909399;">—</span>
|
<span v-else style="color:#909399;">—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="分账给谁" min-width="168">
|
<el-table-column label="分账给谁" min-width="180">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<template v-if="s.row.receiver_details && s.row.receiver_details.length">
|
<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 v-for="(r, idx) in s.row.receiver_details" :key="idx" class="recv-block">
|
||||||
<div class="recv-line1">
|
<div class="recv-head">
|
||||||
<span class="recv-role">{{ r.role_label }}</span>
|
<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>
|
<span class="recv-amt">¥{{ formatMoney(r.amount) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="scroll-cell recv-name-line" @wheel="scrollCellWheel">
|
||||||
|
<span class="scroll-cell-inner">{{ r.display_name }}</span>
|
||||||
|
</div>
|
||||||
<div class="recv-line2 scroll-cell" @wheel="scrollCellWheel" v-if="r.account">
|
<div class="recv-line2 scroll-cell" @wheel="scrollCellWheel" v-if="r.account">
|
||||||
<span class="scroll-cell-inner mono-text">{{ r.account }}</span>
|
<span class="scroll-cell-inner mono-text">{{ r.account }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,9 +293,19 @@
|
|||||||
<span :class="'status-tag s' + s.row.status">{{ s.row.status_text }}</span>
|
<span :class="'status-tag s' + s.row.status">{{ s.row.status_text }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="失败原因" min-width="120">
|
<el-table-column label="失败原因" min-width="168">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div v-if="s.row.fail_reason" class="fail-text" :title="s.row.fail_reason">{{ s.row.fail_reason }}</div>
|
<div v-if="s.row.fail_reason_full || s.row.fail_reason" class="fail-cell">
|
||||||
|
<div class="fail-brief">{{ s.row.fail_reason || s.row.fail_reason_full }}</div>
|
||||||
|
<el-popover
|
||||||
|
v-if="s.row.fail_reason_full"
|
||||||
|
placement="left"
|
||||||
|
width="380"
|
||||||
|
trigger="click">
|
||||||
|
<pre class="fail-pop-full">{{ s.row.fail_reason_full }}</pre>
|
||||||
|
<span slot="reference" class="fail-more">查看完整原因</span>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
<span v-else style="color:#909399;">—</span>
|
<span v-else style="color:#909399;">—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户