```
feat(split): 新增分账接收方明细功能 新增 SplitOrderHelper 类用于同步分账接收方明细数据,包括接收方角色、 姓名、账户和金额等信息。优化管理后台分账订单列表页面,显示详细的 接收方信息和失败原因,提升分账流程的可视化和可追溯性。 ```
这个提交包含在:
@@ -3,6 +3,7 @@
|
|||||||
namespace addons\shopro\library\split;
|
namespace addons\shopro\library\split;
|
||||||
|
|
||||||
use addons\shopro\model\Order;
|
use addons\shopro\model\Order;
|
||||||
|
use addons\shopro\library\split\SplitOrderHelper;
|
||||||
use addons\shopro\model\split\SplitOrder;
|
use addons\shopro\model\split\SplitOrder;
|
||||||
use addons\shopro\model\split\SplitOrderReceiver;
|
use addons\shopro\model\split\SplitOrderReceiver;
|
||||||
use think\Db;
|
use think\Db;
|
||||||
@@ -224,5 +225,6 @@ class SplitExecutor
|
|||||||
$splitOrder->next_retry_time = time() + $this->config->getRetryInterval();
|
$splitOrder->next_retry_time = time() + $this->config->getRetryInterval();
|
||||||
$splitOrder->save();
|
$splitOrder->save();
|
||||||
}
|
}
|
||||||
|
SplitOrderHelper::syncReceivers($splitOrder->id, $items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\library\split;
|
||||||
|
|
||||||
|
use addons\shopro\model\split\SplitOrderReceiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分账订单明细写入(成功/失败均需落库,便于后台展示)
|
||||||
|
*/
|
||||||
|
class SplitOrderHelper
|
||||||
|
{
|
||||||
|
public static function syncReceivers($splitOrderId, array $items, $replace = false)
|
||||||
|
{
|
||||||
|
if (!$splitOrderId || empty($items)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($replace) {
|
||||||
|
SplitOrderReceiver::where('split_order_id', $splitOrderId)->delete();
|
||||||
|
}
|
||||||
|
$exists = SplitOrderReceiver::where('split_order_id', $splitOrderId)->count();
|
||||||
|
if ($exists > 0 && !$replace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$amount = floatval($item['amount'] ?? 0);
|
||||||
|
if ($amount <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SplitOrderReceiver::create([
|
||||||
|
'split_order_id' => $splitOrderId,
|
||||||
|
'receiver_id' => intval($item['receiver_id'] ?? 0),
|
||||||
|
'role_type' => $item['role_type'] ?? '',
|
||||||
|
'amount' => round($amount, 2),
|
||||||
|
'description' => $item['description'] ?? '',
|
||||||
|
'status' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,4 +8,9 @@ class SplitOrderReceiver extends Model
|
|||||||
{
|
{
|
||||||
protected $name = 'shopro_split_order_receiver';
|
protected $name = 'shopro_split_order_receiver';
|
||||||
protected $autoWriteTimestamp = false;
|
protected $autoWriteTimestamp = false;
|
||||||
|
|
||||||
|
public function receiverConfig()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Receiver::class, 'receiver_id', 'id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace addons\shopro_huifu\library;
|
namespace addons\shopro_huifu\library;
|
||||||
|
|
||||||
use addons\shopro\library\split\SplitCalculator;
|
use addons\shopro\library\split\SplitCalculator;
|
||||||
|
use addons\shopro\library\split\SplitOrderHelper;
|
||||||
use addons\shopro\library\split\Config as SplitConfig;
|
use addons\shopro\library\split\Config as SplitConfig;
|
||||||
use addons\shopro\model\Order;
|
use addons\shopro\model\Order;
|
||||||
use addons\shopro\model\split\SplitOrder;
|
use addons\shopro\model\split\SplitOrder;
|
||||||
@@ -469,7 +470,7 @@ class HuifuSplitService
|
|||||||
$splitOrder = SplitOrder::where('order_id', $orderId)->where('channel', 'huifu')->find();
|
$splitOrder = SplitOrder::where('order_id', $orderId)->where('channel', 'huifu')->find();
|
||||||
$splitConfig = new SplitConfig();
|
$splitConfig = new SplitConfig();
|
||||||
if (!$splitOrder) {
|
if (!$splitOrder) {
|
||||||
SplitOrder::create([
|
$splitOrder = SplitOrder::create([
|
||||||
'order_id' => $orderId,
|
'order_id' => $orderId,
|
||||||
'order_sn' => $order['order_sn'],
|
'order_sn' => $order['order_sn'],
|
||||||
'channel' => 'huifu',
|
'channel' => 'huifu',
|
||||||
@@ -489,5 +490,6 @@ class HuifuSplitService
|
|||||||
$splitOrder->next_retry_time = time() + $splitConfig->getRetryInterval();
|
$splitOrder->next_retry_time = time() + $splitConfig->getRetryInterval();
|
||||||
$splitOrder->save();
|
$splitOrder->save();
|
||||||
}
|
}
|
||||||
|
SplitOrderHelper::syncReceivers($splitOrder->id, $items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace app\admin\controller\shopro\split;
|
|||||||
|
|
||||||
use app\admin\controller\shopro\Base;
|
use app\admin\controller\shopro\Base;
|
||||||
use addons\shopro\library\split\Config as SplitConfig;
|
use addons\shopro\library\split\Config as SplitConfig;
|
||||||
|
use addons\shopro\library\split\SplitCalculator;
|
||||||
use addons\shopro\library\split\SplitExecutor;
|
use addons\shopro\library\split\SplitExecutor;
|
||||||
use addons\shopro\library\split\ProfitSharingService;
|
use addons\shopro\library\split\ProfitSharingService;
|
||||||
use app\admin\model\shopro\order\Order as ShopOrder;
|
use app\admin\model\shopro\order\Order as ShopOrder;
|
||||||
@@ -38,7 +39,9 @@ class Order extends Base
|
|||||||
$total = $query->where($where)->count();
|
$total = $query->where($where)->count();
|
||||||
$list = $this->buildSearch()
|
$list = $this->buildSearch()
|
||||||
->with([
|
->with([
|
||||||
'receivers',
|
'receivers' => function ($q) {
|
||||||
|
$q->with('receiverConfig');
|
||||||
|
},
|
||||||
'order' => function ($q) {
|
'order' => function ($q) {
|
||||||
$q->field('id,order_sn,user_id,pay_fee,paytime,status')
|
$q->field('id,order_sn,user_id,pay_fee,paytime,status')
|
||||||
->with(['user' => function ($uq) {
|
->with(['user' => function ($uq) {
|
||||||
@@ -110,14 +113,18 @@ class Order extends Base
|
|||||||
$buyerNickname = $user['nickname'] ?? '';
|
$buyerNickname = $user['nickname'] ?? '';
|
||||||
$buyerMobile = $user['mobile'] ?? '';
|
$buyerMobile = $user['mobile'] ?? '';
|
||||||
|
|
||||||
|
$orderId = intval($data['order_id'] ?? 0);
|
||||||
$receivers = $data['receivers'] ?? [];
|
$receivers = $data['receivers'] ?? [];
|
||||||
$lines = [];
|
$receiverDetails = $this->buildReceiverDetails($orderId, $receivers);
|
||||||
foreach ($receivers as $recv) {
|
$receiverPreview = empty($receivers) && !empty($receiverDetails);
|
||||||
$r = is_array($recv) ? $recv : (method_exists($recv, 'toArray') ? $recv->toArray() : []);
|
|
||||||
$lines[] = sprintf(
|
$summaryLines = [];
|
||||||
'%s ¥%s',
|
foreach ($receiverDetails as $detail) {
|
||||||
$this->roleTypeLabel($r['role_type'] ?? ''),
|
$summaryLines[] = sprintf(
|
||||||
number_format(floatval($r['amount'] ?? 0), 2, '.', '')
|
'%s %s ¥%s',
|
||||||
|
$detail['role_label'],
|
||||||
|
$detail['display_name'],
|
||||||
|
number_format(floatval($detail['amount']), 2, '.', '')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,15 +143,169 @@ class Order extends Base
|
|||||||
$data['order_paytime_text'] = (is_array($order) && !empty($order['paytime']))
|
$data['order_paytime_text'] = (is_array($order) && !empty($order['paytime']))
|
||||||
? date('Y-m-d H:i:s', $order['paytime'])
|
? date('Y-m-d H:i:s', $order['paytime'])
|
||||||
: '-';
|
: '-';
|
||||||
$data['receiver_summary'] = $lines ? implode(';', $lines) : '—';
|
$data['receiver_details'] = $receiverDetails;
|
||||||
|
$data['receiver_preview'] = $receiverPreview;
|
||||||
|
$data['receiver_summary'] = $summaryLines ? implode(';', $summaryLines) : '—';
|
||||||
|
$data['fail_reason'] = $this->formatFailReason($data['wx_result'] ?? '', $status);
|
||||||
|
|
||||||
unset($data['order']);
|
unset($data['order'], $data['receivers']);
|
||||||
$rows[] = $data;
|
$rows[] = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildReceiverDetails($orderId, $receivers)
|
||||||
|
{
|
||||||
|
$details = [];
|
||||||
|
if (!empty($receivers)) {
|
||||||
|
$userIds = [];
|
||||||
|
foreach ($receivers as $recv) {
|
||||||
|
$r = is_array($recv) ? $recv : (method_exists($recv, 'toArray') ? $recv->toArray() : []);
|
||||||
|
$cfg = $r['receiver_config'] ?? null;
|
||||||
|
if (is_object($cfg) && method_exists($cfg, 'toArray')) {
|
||||||
|
$cfg = $cfg->toArray();
|
||||||
|
}
|
||||||
|
$roleId = intval(is_array($cfg) ? ($cfg['role_id'] ?? 0) : 0);
|
||||||
|
if ($roleId > 0) {
|
||||||
|
$userIds[] = $roleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$users = $userIds ? $this->loadUsersByIds($userIds) : [];
|
||||||
|
|
||||||
|
foreach ($receivers as $recv) {
|
||||||
|
$r = is_array($recv) ? $recv : (method_exists($recv, 'toArray') ? $recv->toArray() : []);
|
||||||
|
$cfg = $r['receiver_config'] ?? null;
|
||||||
|
if (is_object($cfg) && method_exists($cfg, 'toArray')) {
|
||||||
|
$cfg = $cfg->toArray();
|
||||||
|
}
|
||||||
|
$cfg = is_array($cfg) ? $cfg : [];
|
||||||
|
$roleId = intval($cfg['role_id'] ?? 0);
|
||||||
|
$account = trim((string) ($cfg['receiver_account'] ?? ''));
|
||||||
|
$recvName = trim((string) ($cfg['receiver_name'] ?? ''));
|
||||||
|
$user = $roleId && isset($users[$roleId]) ? $users[$roleId] : null;
|
||||||
|
$details[] = [
|
||||||
|
'role_label' => $this->roleTypeLabel($r['role_type'] ?? ($cfg['role_type'] ?? '')),
|
||||||
|
'display_name' => $this->formatReceiverDisplayName($roleId, $recvName, $user),
|
||||||
|
'account' => $account,
|
||||||
|
'amount' => floatval($r['amount'] ?? 0),
|
||||||
|
'status' => intval($r['status'] ?? 0),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($orderId <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$items = (new SplitCalculator())->calculate($orderId);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (empty($items)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$roleIds = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rid = intval($item['role_id'] ?? 0);
|
||||||
|
if ($rid > 0) {
|
||||||
|
$roleIds[] = $rid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$users = $roleIds ? $this->loadUsersByIds($roleIds) : [];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$roleId = intval($item['role_id'] ?? 0);
|
||||||
|
$user = $roleId && isset($users[$roleId]) ? $users[$roleId] : null;
|
||||||
|
$details[] = [
|
||||||
|
'role_label' => $this->roleTypeLabel($item['role_type'] ?? ''),
|
||||||
|
'display_name' => $this->formatReceiverDisplayName(
|
||||||
|
$roleId,
|
||||||
|
trim((string) ($item['receiver_name'] ?? '')),
|
||||||
|
$user
|
||||||
|
),
|
||||||
|
'account' => trim((string) ($item['receiver_account'] ?? '')),
|
||||||
|
'amount' => floatval($item['amount'] ?? 0),
|
||||||
|
'status' => -1,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadUsersByIds(array $userIds)
|
||||||
|
{
|
||||||
|
$userIds = array_values(array_unique(array_filter(array_map('intval', $userIds))));
|
||||||
|
if (!$userIds) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$rows = ShopUser::where('id', 'in', $userIds)->field('id,nickname,mobile')->select();
|
||||||
|
$map = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$item = is_array($row) ? $row : $row->toArray();
|
||||||
|
$map[intval($item['id'])] = $item;
|
||||||
|
}
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatReceiverDisplayName($roleId, $receiverName, $user)
|
||||||
|
{
|
||||||
|
$nickname = is_array($user) ? trim((string) ($user['nickname'] ?? '')) : '';
|
||||||
|
$mobile = is_array($user) ? trim((string) ($user['mobile'] ?? '')) : '';
|
||||||
|
if ($nickname !== '') {
|
||||||
|
$label = $nickname;
|
||||||
|
if ($roleId > 0) {
|
||||||
|
$label .= '#' . $roleId;
|
||||||
|
}
|
||||||
|
if ($mobile !== '') {
|
||||||
|
$label .= ' ' . $this->maskMobile($mobile);
|
||||||
|
}
|
||||||
|
return $label;
|
||||||
|
}
|
||||||
|
if ($receiverName !== '') {
|
||||||
|
return $roleId > 0 ? ($receiverName . '#' . $roleId) : $receiverName;
|
||||||
|
}
|
||||||
|
return $roleId > 0 ? ('用户#' . $roleId) : '通用接收方';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function maskMobile($mobile)
|
||||||
|
{
|
||||||
|
$mobile = trim((string) $mobile);
|
||||||
|
if (strlen($mobile) >= 11) {
|
||||||
|
return substr($mobile, 0, 3) . '****' . substr($mobile, -4);
|
||||||
|
}
|
||||||
|
return $mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatFailReason($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'] ?? '',
|
||||||
|
$decoded['resp_code'] ?? '',
|
||||||
|
$decoded['bank_message'] ?? '',
|
||||||
|
]);
|
||||||
|
if ($parts) {
|
||||||
|
return implode(' ', $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mb_strlen($raw) > 120 ? (mb_substr($raw, 0, 120) . '…') : $raw;
|
||||||
|
}
|
||||||
|
|
||||||
protected function formatBuyerDisplay($nickname, $mobile, $userId)
|
protected function formatBuyerDisplay($nickname, $mobile, $userId)
|
||||||
{
|
{
|
||||||
$nickname = trim((string) $nickname);
|
$nickname = trim((string) $nickname);
|
||||||
|
|||||||
@@ -26,17 +26,28 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.source-main {
|
.cell-main {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #333;
|
color: #303133;
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
word-break: keep-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.source-sub {
|
.cell-sub {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
line-height: 1.6;
|
line-height: 1.5;
|
||||||
margin-top: 2px;
|
margin-top: 4px;
|
||||||
|
word-break: keep-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.mono-text {
|
||||||
|
font-family: Consolas, Monaco, "Courier New", monospace;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
}
|
}
|
||||||
.link-text {
|
.link-text {
|
||||||
color: #7438D5;
|
color: #7438D5;
|
||||||
@@ -44,22 +55,76 @@
|
|||||||
}
|
}
|
||||||
.link-text:hover { opacity: 0.85; }
|
.link-text:hover { opacity: 0.85; }
|
||||||
.money-text { color: #e1212b; font-weight: 600; }
|
.money-text { color: #e1212b; font-weight: 600; }
|
||||||
|
.channel-tag {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #f4f4f5;
|
||||||
|
color: #606266;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
.status-tag {
|
.status-tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.status-tag.s0 { background: #f4f4f5; color: #909399; }
|
.status-tag.s0 { background: #f4f4f5; color: #909399; }
|
||||||
.status-tag.s1 { background: #ecf5ff; color: #409eff; }
|
.status-tag.s1 { background: #ecf5ff; color: #409eff; }
|
||||||
.status-tag.s2 { background: #f0f9eb; color: #67c23a; }
|
.status-tag.s2 { background: #f0f9eb; color: #67c23a; }
|
||||||
.status-tag.s3 { background: #fdf6ec; color: #e6a23c; }
|
.status-tag.s3 { background: #fdf6ec; color: #e6a23c; }
|
||||||
.status-tag.s4 { background: #fef0f0; color: #f56c6c; }
|
.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: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
.op-hint {
|
.op-hint {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
#splitOrderIndex .el-table .cell {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="splitOrderIndex" v-cloak>
|
<div id="splitOrderIndex" v-cloak>
|
||||||
<div class="page-card">
|
<div class="page-card">
|
||||||
@@ -92,44 +157,57 @@
|
|||||||
<div class="page-card" style="padding-bottom: 8px;">
|
<div class="page-card" style="padding-bottom: 8px;">
|
||||||
<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="158"></el-table-column>
|
<el-table-column label="记录时间" prop="createtime_text" width="156"></el-table-column>
|
||||||
<el-table-column label="买家" min-width="140">
|
<el-table-column label="买家" min-width="130" show-overflow-tooltip>
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div class="source-main">{{ s.row.buyer_display || '—' }}</div>
|
<div class="cell-main">{{ s.row.buyer_display || '—' }}</div>
|
||||||
<div class="source-sub" v-if="s.row.buyer_nickname && s.row.buyer_mobile">手机 {{ s.row.buyer_mobile }}</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="订单 / 分账" min-width="220">
|
<el-table-column label="订单号" min-width="200" show-overflow-tooltip>
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div class="source-main">
|
<div class="cell-main">
|
||||||
<span class="link-text" @click="openOrder(s.row.order_id)">{{ s.row.order_sn || '—' }}</span>
|
<span class="link-text mono-text" @click="openOrder(s.row.order_id)">{{ s.row.order_sn || '—' }}</span>
|
||||||
<span class="source-sub" style="display:inline;margin-left:6px;">{{ s.row.channel_text }}</span>
|
<span class="channel-tag">{{ s.row.channel_text }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="source-sub">分账单号 {{ s.row.out_order_no || '—' }}</div>
|
<div class="cell-sub mono-text" v-if="s.row.out_order_no">分账 {{ s.row.out_order_no }}</div>
|
||||||
<div class="source-sub" v-if="s.row.order_paytime_text !== '-'">支付 {{ s.row.order_paytime_text }}</div>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="金额" width="118" align="right">
|
<el-table-column label="金额" width="112" align="right">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<div>订单 <span class="money-text">¥{{ formatMoney(s.row.total_amount) }}</span></div>
|
<div class="cell-main">订单 <span class="money-text">¥{{ formatMoney(s.row.total_amount) }}</span></div>
|
||||||
<div class="source-sub">分账 ¥{{ formatMoney(s.row.split_amount) }}</div>
|
<div class="cell-sub">分账 ¥{{ formatMoney(s.row.split_amount) }}</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="接收方明细" min-width="160" show-overflow-tooltip>
|
<el-table-column label="分账接收方" min-width="260">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<span class="source-sub">{{ s.row.receiver_summary || '—' }}</span>
|
<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>
|
||||||
|
<span class="recv-name">{{ r.display_name }}</span>
|
||||||
|
<span class="recv-account" v-if="r.account">{{ r.account }}</span>
|
||||||
|
<span class="recv-amt">¥{{ formatMoney(r.amount) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="s.row.receiver_preview" class="recv-preview-hint">按当前规则预估(历史失败单未落库明细)</div>
|
||||||
|
</div>
|
||||||
|
<span v-else class="cell-sub">—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="96" align="center">
|
<el-table-column label="状态" width="88" align="center">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<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="重试" width="64" align="center" prop="retry_count"></el-table-column>
|
<el-table-column label="失败原因" min-width="140" show-overflow-tooltip>
|
||||||
<el-table-column label="操作" width="168" fixed="right">
|
<template slot-scope="s">
|
||||||
|
<div v-if="s.row.fail_reason" class="fail-reason" :title="s.row.fail_reason">{{ s.row.fail_reason }}</div>
|
||||||
|
<span v-else class="cell-sub">—</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">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<el-button type="text" v-if="s.row.status == 4" @click="retry(s.row)">重试分账</el-button>
|
<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" @click="query(s.row.id)">同步状态</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -146,7 +224,7 @@
|
|||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
<p class="op-hint" style="margin-top:10px;">
|
<p class="op-hint" style="margin-top:10px;">
|
||||||
「重试分账」仅对<strong>失败</strong>记录有效,会向通道重新发起分账;「同步通道状态」只拉取最新结果并更新本页,不会重复扣款。
|
「重试分账」仅对<strong>失败</strong>记录有效,会向通道重新发起分账;「同步状态」只拉取最新结果并更新本页,不会重复扣款。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户