```
feat(split): 添加分账手动指定接收方功能 - 实现SplitManualService服务类,支持手动覆盖分账明细 - 新增SplitManual和SplitManualLog数据模型 - 修改SplitCalculator优先使用手动覆盖数据进行分账计算 - 添加分账记录手动指定接收方的数据库表结构 - 集成手动分账到汇付分账流程中,支持失败重试机制 - 添加商品详情内容更新SQL脚本用于汇付审核 ```
这个提交包含在:
@@ -108,6 +108,18 @@
|
||||
}
|
||||
.source-tag.commission { background: #ecf5ff; color: #409eff; }
|
||||
.source-tag.rule { background: #fdf6ec; color: #e6a23c; }
|
||||
.source-tag.manual { background: #f0ebfa; color: #7438D5; }
|
||||
.manual-badge {
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
padding: 0 5px;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
border-radius: 3px;
|
||||
background: #fef0f0;
|
||||
color: #f56c6c;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.source-detail-btn {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
@@ -187,6 +199,57 @@
|
||||
.diag-pop li { margin-bottom: 6px; }
|
||||
.diag-pop li:last-child { margin-bottom: 0; }
|
||||
.op-hint { font-size: 12px; color: #909399; margin-top: 10px; }
|
||||
.manual-dialog .section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.manual-dialog .section-block {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.manual-dialog .orig-item {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
line-height: 1.7;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
.manual-dialog .orig-item:last-child { border-bottom: none; }
|
||||
.manual-dialog .sum-row {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
color: #303133;
|
||||
}
|
||||
.manual-dialog .sum-row strong { color: #e1212b; }
|
||||
.history-drawer .history-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.history-drawer .history-item:last-child { border-bottom: none; }
|
||||
.history-drawer .history-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.history-drawer .history-action {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
font-size: 13px;
|
||||
}
|
||||
.history-drawer .history-time { font-size: 12px; color: #909399; }
|
||||
.history-drawer .history-meta { font-size: 12px; color: #606266; margin-bottom: 6px; }
|
||||
.history-drawer .items-diff { font-size: 12px; line-height: 1.65; color: #606266; }
|
||||
.history-drawer .items-diff .arrow { color: #909399; margin: 0 4px; }
|
||||
.history-drawer .retry-result { margin-top: 6px; font-size: 12px; }
|
||||
.history-drawer .retry-result.success { color: #67c23a; }
|
||||
.history-drawer .retry-result.fail { color: #f56c6c; }
|
||||
.history-drawer .retry-result.pending { color: #409eff; }
|
||||
</style>
|
||||
<div id="splitOrderIndex" v-cloak>
|
||||
<div class="page-card">
|
||||
@@ -249,14 +312,15 @@
|
||||
<span class="money-num highlight">¥{{ formatMoney(s.row.split_amount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分账来源" class-name="col-source" width="118" align="center">
|
||||
<el-table-column label="分账来源" class-name="col-source" width="130" align="center">
|
||||
<template slot-scope="s">
|
||||
<div class="source-cell" v-if="s.row.split_source">
|
||||
<span :class="'source-tag ' + (s.row.split_source === 'commission' ? 'commission' : 'rule')">
|
||||
{{ s.row.split_source === 'commission' ? '按佣金' : '规则兜底' }}
|
||||
<div class="source-cell" v-if="s.row.split_source === 'manual' || s.row.has_manual_override || s.row.calc_source">
|
||||
<span class="source-tag manual" v-if="s.row.split_source === 'manual' || s.row.has_manual_override">手动</span>
|
||||
<span v-else-if="s.row.calc_source" :class="'source-tag ' + (s.row.calc_source === 'commission' ? 'commission' : 'rule')">
|
||||
{{ s.row.calc_source === 'commission' ? '按佣金' : '规则兜底' }}
|
||||
</span>
|
||||
<el-popover
|
||||
v-if="s.row.split_source === 'rule' && s.row.split_diagnose && s.row.split_diagnose.length"
|
||||
v-if="s.row.calc_source === 'rule' && s.row.split_diagnose && s.row.split_diagnose.length && s.row.split_source !== 'manual'"
|
||||
placement="left"
|
||||
width="300"
|
||||
trigger="click">
|
||||
@@ -310,8 +374,10 @@
|
||||
<span v-else style="color:#909399;">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="128" fixed="right">
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template slot-scope="s">
|
||||
<el-button type="text" size="small" v-if="s.row.can_edit_manual" @click="openManualDialog(s.row)">指定接收方</el-button>
|
||||
<el-button type="text" size="small" v-if="s.row.can_edit_manual && (s.row.has_manual_override || s.row.split_source === 'manual')" @click="openHistoryDrawer(s.row)">变更历史</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>
|
||||
@@ -329,8 +395,98 @@
|
||||
@size-change="onSizeChange">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<p class="op-hint">「重试」仅对失败记录向通道重新发起;「同步」只更新状态,不会重复扣款。</p>
|
||||
<p class="op-hint">失败汇付单可「指定接收方」改人改金额后重试;「同步」只更新状态,不会重复扣款。</p>
|
||||
</div>
|
||||
|
||||
<!-- 指定接收方弹窗 -->
|
||||
<el-dialog title="指定接收方" :visible.sync="manualDialogVisible" width="720px" :close-on-click-modal="false" custom-class="manual-dialog">
|
||||
<div v-loading="manualLoading">
|
||||
<p style="font-size:13px;color:#606266;margin:0 0 12px;">
|
||||
订单 <strong>{{ manualContext.order_sn }}</strong>
|
||||
· 确认基数 ¥{{ formatMoney(manualContext.confirm_base) }}
|
||||
· 上限 ¥{{ formatMoney(manualContext.max_split_amount) }}
|
||||
</p>
|
||||
<div class="section-block" v-if="manualContext.original_items && manualContext.original_items.length">
|
||||
<p class="section-title">原自动计算明细</p>
|
||||
<div class="orig-item" v-for="(it, idx) in manualContext.original_items" :key="'orig'+idx">
|
||||
{{ formatManualItemLine(it) }}
|
||||
</div>
|
||||
</div>
|
||||
<p class="section-title">手动指定明细</p>
|
||||
<el-table :data="manualForm.items" border size="small" style="width:100%;margin-bottom:10px;">
|
||||
<el-table-column label="接收方" min-width="220">
|
||||
<template slot-scope="s">
|
||||
<el-select v-model="s.row.receiver_id" filterable placeholder="选择汇付接收方" size="small" style="width:100%;" @change="onReceiverChange(s.row)">
|
||||
<el-option v-for="opt in manualContext.receiver_options" :key="opt.id" :label="opt.label" :value="opt.id"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="角色" width="100">
|
||||
<template slot-scope="s">
|
||||
<el-select v-model="s.row.role_type" size="small" style="width:100%;">
|
||||
<el-option label="推广员" value="promoter"></el-option>
|
||||
<el-option label="区域公司" value="regional_agent"></el-option>
|
||||
<el-option label="平台" value="platform"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="120">
|
||||
<template slot-scope="s">
|
||||
<el-input-number v-model="s.row.amount" :min="0.01" :precision="2" :step="0.01" size="small" controls-position="right" style="width:100%;"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" width="120">
|
||||
<template slot-scope="s">
|
||||
<el-input v-model="s.row.description" size="small" placeholder="备注"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="" width="56" align="center">
|
||||
<template slot-scope="s">
|
||||
<el-button type="text" size="small" style="color:#f56c6c;" @click="removeManualRow(s.$index)">删</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-button size="small" @click="addManualRow">+ 添加一行</el-button>
|
||||
<div class="sum-row">合计 <strong>¥{{ formatMoney(manualItemsTotal) }}</strong></div>
|
||||
<el-input v-model="manualForm.remark" type="textarea" :rows="2" placeholder="操作备注(可选)" style="margin-top:12px;"></el-input>
|
||||
</div>
|
||||
<span slot="footer">
|
||||
<el-button size="small" @click="manualDialogVisible = false">取消</el-button>
|
||||
<el-button size="small" v-if="manualContext.has_manual" @click="clearManualOverride">清除手动指定</el-button>
|
||||
<el-button size="small" type="primary" plain :loading="manualSaving" @click="saveManual(false)">仅保存</el-button>
|
||||
<el-button size="small" type="primary" :loading="manualSaving" @click="saveManual(true)">保存并重试</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 变更历史抽屉 -->
|
||||
<el-drawer title="变更历史" :visible.sync="historyDrawerVisible" size="480px" custom-class="history-drawer">
|
||||
<div v-loading="historyLoading" style="padding:0 20px 20px;">
|
||||
<div v-if="!historyList.length && !historyLoading" style="color:#909399;font-size:13px;text-align:center;padding:40px 0;">暂无变更记录</div>
|
||||
<div class="history-item" v-for="h in historyList" :key="h.id">
|
||||
<div class="history-head">
|
||||
<span class="history-action">{{ h.action_text }}</span>
|
||||
<span class="history-time">{{ h.createtime_text }}</span>
|
||||
</div>
|
||||
<div class="history-meta" v-if="h.admin_name">操作人:{{ h.admin_name }}</div>
|
||||
<div class="history-meta" v-if="h.remark">备注:{{ h.remark }}</div>
|
||||
<div class="items-diff">
|
||||
<div v-if="h.before_items && h.before_items.length">
|
||||
<div v-for="(it, i) in h.before_items" :key="'b'+i">{{ formatManualItemLine(it) }}</div>
|
||||
</div>
|
||||
<span v-else style="color:#c0c4cc;">(空)</span>
|
||||
<span class="arrow">→</span>
|
||||
<div v-if="h.after_items && h.after_items.length">
|
||||
<div v-for="(it, i) in h.after_items" :key="'a'+i">{{ formatManualItemLine(it) }}</div>
|
||||
</div>
|
||||
<span v-else style="color:#c0c4cc;">(空)</span>
|
||||
</div>
|
||||
<div v-if="h.retry_result" :class="'retry-result ' + h.retry_result">
|
||||
重试结果:{{ h.retry_result_text }}
|
||||
<span v-if="h.retry_message"> — {{ h.retry_message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
<script src="__CDN__/assets/addons/shopro/libs/vue.js"></script>
|
||||
<script src="__CDN__/assets/addons/shopro/libs/element/element.js"></script>
|
||||
|
||||
在新工单中引用
屏蔽一个用户