Переглянути джерело

feat(admin): 优化分账订单管理界面及数据展示

- 调整搜索表单字段宽度,提升布局美观度
- 重新设计表格列结构,增加更多分账相关字段展示
- 新增分账金额计算逻辑与手续费扣除明细展示
- 引入金额格式化方法支持分转元显示
- 优化详情弹窗样式及数据呈现方式
- 增强状态标签样式区分度,便于识别不同分账阶段
- 添加表格与卡片组件的视觉美化效果
- 修复数值计算精度问题,确保分账金额准确无误
- 定义 separate_value 字段类型为 float 提高数据准确性
runphp 4 місяців тому
батько
коміт
d492241a38

+ 255 - 17
resource/admin/ProfitShareOrder.vue

@@ -8,11 +8,12 @@
             v-model="searchForm.out_separate_no" 
             placeholder="请输入商户分账指令流水号" 
             clearable
+            style="width: 300px"
             @keyup.enter="handleSearch"
           />
         </el-form-item>
         <el-form-item label="分账状态">
-          <el-select v-model="searchForm.status" placeholder="请选择分账状态" clearable>
+          <el-select v-model="searchForm.status" placeholder="请选择分账状态" clearable style="width: 150px">
             <el-option label="全部" value="" />
             <el-option label="待处理" value="PENDING" />
             <el-option label="处理中" value="PROCESSING" />
@@ -41,23 +42,58 @@
         :data="tableData"
         border
         stripe
+        style="width: 100%"
+        :header-cell-style="{background: '#f8f9fa', color: '#606266'}"
+        highlight-current-row
       >
-        <el-table-column prop="out_separate_no" label="商户分账指令流水号" min-width="200" />
-        <el-table-column prop="user_id" label="用户ID" width="100" />
-        <el-table-column prop="total_amt" label="订单金额(分)" width="120">
+        <el-table-column prop="id" label="ID" width="80" align="center" />
+        <el-table-column prop="out_separate_no" label="商户分账指令流水号" min-width="220" />
+        <el-table-column prop="user_id" label="用户ID" width="100" align="center" />
+        <el-table-column prop="merchant_no" label="分账商户号" width="160" />
+        <el-table-column prop="total_amt" label="分账总金额(元)" width="140" align="center">
           <template #default="{ row }">
-            {{ formatAmount(row.total_amt) }}
+            <span style="font-weight: 600; color: #e6a23c; font-size: 15px">{{ formatAmountToYuan(row.total_amt) }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="recv_no" label="分账接收方编号" width="150" />
-        <el-table-column prop="status" label="分账状态" width="100">
+        <el-table-column label="接收方商户信息" width="180">
           <template #default="{ row }">
-            {{ getStatusText(row.status) }}
+            <div class="merchant-info">
+              <div class="merchant-no">商户号: {{ row.recv_merchant_no }}</div>
+              <div class="share-amount">分账金额: ¥{{ formatAmountToYuan(row.total_amt - row.separate_value) }}</div>
+            </div>
           </template>
         </el-table-column>
-        <el-table-column prop="fail_reason" label="分账失败原因" min-width="150" />
-        <el-table-column prop="create_time" label="创建时间" width="180" />
-        <el-table-column prop="update_time" label="更新时间" width="180" />
+        <el-table-column label="分账接收方信息" width="180">
+          <template #default="{ row }">
+            <div class="receiver-info">
+              <div class="receiver-no">编号: {{ row.recv_no }}</div>
+              <div class="amount-details">
+                <div class="amount-row">
+                  <span class="label">总金额:</span>
+                  <span class="value">¥{{ formatAmountToYuan(getTotalSeparateAmount(row)) }}</span>
+                </div>
+                <div class="amount-row">
+                  <span class="label">手续费:</span>
+                  <span class="value fee">-¥{{ formatAmountToYuan(row.fee_amt) }}</span>
+                </div>
+                <div class="divider"></div>
+                <div class="amount-row total">
+                  <span class="label">实际分账:</span>
+                  <span class="value total">¥{{ formatAmountToYuan(row.separate_value) }}</span>
+                </div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="recv_no" label="分账接收方编号" width="160" />
+        <el-table-column prop="status" label="分账状态" width="120" align="center">
+          <template #default="{ row }">
+            <span :class="['status-badge', `status-${row.status.toLowerCase()}`]">{{ getStatusText(row.status) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="fail_reason" label="分账失败原因" min-width="180" />
+        <el-table-column prop="create_time" label="创建时间" width="180" align="center" />
+        <el-table-column prop="update_time" label="更新时间" width="180" align="center" />
         <el-table-column label="操作" width="150" fixed="right">
           <template #default="{ row }">
             <el-button type="primary" size="small" @click="handleView(row)">详情</el-button>
@@ -97,8 +133,28 @@
         <el-form-item label="用户ID:">
           <span>{{ detailData.user_id }}</span>
         </el-form-item>
-        <el-form-item label="订单金额(分):">
-          <span>{{ formatAmount(detailData.total_amt) }}</span>
+        <el-form-item label="分账总金额(元):">
+          <span>{{ formatAmountToYuan(detailData.total_amt) }}</span>
+        </el-form-item>
+        <el-form-item label="用户分账金额:">
+          <div class="amount-calculation detail">
+            <div class="main-amount">
+              <span>总金额:</span>
+              <span class="amount">¥{{ formatAmountToYuan(getTotalSeparateAmount(detailData)) }}</span>
+            </div>
+            <div class="fee-amount">
+              <span>- 手续费:</span>
+              <span class="amount">¥{{ formatAmountToYuan(detailData.fee_amt) }}</span>
+            </div>
+            <div class="divider"></div>
+            <div class="result-amount">
+              <span>实际分账:</span>
+              <span class="amount">¥{{ formatAmountToYuan(detailData.separate_value) }}</span>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="商户分账金额(元):">
+          <span>{{ formatAmountToYuan(getMerchantShareAmount(detailData)) }}</span>
         </el-form-item>
         <el-form-item label="分账接收方编号:">
           <span>{{ detailData.recv_no }}</span>
@@ -128,6 +184,7 @@
 <script>
 export default {
   name: "ProfitShareOrder",
+  inheritAttrs: false,
   props: {
     axiosInstance: {
       type: Object,
@@ -156,9 +213,25 @@ export default {
     this.fetchData()
   },
   methods: {
-    // 格式化金额显示
-    formatAmount(amount) {
-      return amount || amount === 0 ? amount : '-';
+    // 计算用户分账总金额(实际分账+手续费)
+    getTotalSeparateAmount(row) {
+      // 处理字符串和数字类型的金额值
+      const separateValue = parseFloat(row.separate_value) || 0;
+      const feeAmt = parseFloat(row.fee_amt) || 0;
+      return separateValue + feeAmt;
+    },
+
+    // 格式化金额显示(分转元)
+    formatAmountToYuan(amount) {
+      if (amount === null || amount === undefined || amount === '') {
+        return '-';
+      }
+      
+      // 如果是字符串,先转换为浮点数
+      const numericAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
+      
+      // 将分转换为元并保留两位小数
+      return (numericAmount / 100).toFixed(2);
     },
 
     // 获取分账状态文本
@@ -278,10 +351,20 @@ export default {
 
 .search-card {
   margin-bottom: 20px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  border-radius: 8px;
+}
+
+.search-card ::v-deep(.el-card__header) {
+  background-color: #f5f7fa;
+  font-weight: bold;
 }
 
 .table-card {
   margin-bottom: 20px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  border-radius: 8px;
+  overflow: hidden;
 }
 
 .table-header {
@@ -291,12 +374,167 @@ export default {
 }
 
 .header-title {
-  font-size: 16px;
+  font-size: 18px;
   font-weight: bold;
+  color: #303133;
 }
 
 .pagination {
   margin-top: 20px;
   text-align: right;
 }
+
+.amount-calculation {
+  font-size: 12px;
+}
+
+.amount-calculation.detail {
+  padding: 12px;
+  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
+  border-radius: 6px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.amount-calculation .main-amount,
+.amount-calculation .fee-amount,
+.amount-calculation .result-amount {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 5px;
+}
+
+.amount-calculation .amount {
+  font-weight: 600;
+  color: #303133;
+  text-align: right;
+}
+
+.amount-calculation .result-amount .amount {
+  color: #67c23a;
+  font-weight: 700;
+  font-size: 14px;
+}
+
+.amount-calculation .divider {
+  height: 1px;
+  background: linear-gradient(to right, transparent, #c0c4cc, transparent);
+  margin: 6px 0;
+}
+
+.merchant-info {
+  font-size: 12px;
+  padding: 8px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+}
+
+.merchant-info .merchant-no {
+  margin-bottom: 4px;
+  color: #606266;
+}
+
+.merchant-info .share-amount {
+  color: #303133;
+  font-weight: 600;
+}
+
+.receiver-info {
+  padding: 8px;
+  background: linear-gradient(135deg, #e8f4ff 0%, #d0e6ff 100%);
+  border-radius: 4px;
+  font-size: 12px;
+}
+
+.amount-details {
+  margin-top: 5px;
+}
+
+.amount-row {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 3px;
+}
+
+.amount-row .label {
+  color: #606266;
+}
+
+.amount-row .value {
+  font-weight: 500;
+  color: #303133;
+}
+
+.amount-row .value.fee {
+  color: #f56c6c;
+}
+
+.amount-row.total {
+  font-weight: 700;
+  margin-top: 3px;
+  padding-top: 3px;
+  border-top: 1px solid #dcdfe6;
+}
+
+.amount-row.total .value {
+  color: #67c23a;
+  font-size: 14px;
+}
+
+.status-badge {
+  padding: 4px 8px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 500;
+}
+
+.status-pending {
+  background-color: #f0f9eb;
+  color: #67c23a;
+}
+
+.status-processing {
+  background-color: #ecf5ff;
+  color: #409eff;
+}
+
+.status-accepted {
+  background-color: #e6f3ff;
+  color: #3a86ff;
+}
+
+.status-success {
+  background-color: #f0f9eb;
+  color: #67c23a;
+}
+
+.status-fail {
+  background-color: #fef0f0;
+  color: #f56c6c;
+}
+
+.el-table ::v-deep(.el-table__header-wrapper) th {
+  background-color: #f5f7fa;
+  color: #606266;
+  font-weight: 600;
+}
+
+.el-table ::v-deep(.el-table__row:hover) td {
+  background-color: #f5f9ff !important;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  padding: 10px 0;
+}
+
+::v-deep(.el-dialog__header) {
+  background-color: #f5f7fa;
+  border-bottom: 1px solid #ebeef5;
+  font-weight: bold;
+}
+
+::v-deep(.el-form-item__label) {
+  font-weight: 500;
+}
 </style>

+ 1 - 1
src/Entity/ProfitShareOrderEntity.php

@@ -68,7 +68,7 @@ class ProfitShareOrderEntity extends BaseEntity
      */
     private function calculateOrder(float $amount, float $splitLowestRatio): array
     {
-        $splitLowestRatio = bcsub(100, (string)$splitLowestRatio, 2);
+        $splitLowestRatio = bcsub('100', (string)$splitLowestRatio, 2);
         $fee = Config::getConfig('profit_share_fee');
         $feeAmt = bcmul((string)$amount, (string)$fee, 2);
         if (bccomp($feeAmt, '1') == -1) {

+ 1 - 0
src/Model/ProfitShareOrderModel.php

@@ -17,6 +17,7 @@ class ProfitShareOrderModel extends Model
             'type' => [
                 'status' => ProfitShareOrderStatusEnum::class,
                 'cmd_type' => ProfitShareOrderCMDTypeEnum::class,
+                'separate_value' => 'float',
             ],
             'insert' => ['out_separate_no'],
             'readonly' => ['user_id', 'out_separate_no'],